2008-10-28 31 views
11

¿Cómo puedo crear un procedimiento almacenado de Oracle que acepte una cantidad variable de valores de parámetros utilizados para alimentar una cláusula IN?Procedimiento almacenado de Oracle con parámetros para la cláusula IN

Esto es lo que estoy tratando de lograr. No sé cómo declarar en PLSQL para pasar una lista de variables de claves primarias de las filas que deseo actualizar.

FUNCTION EXECUTE_UPDATE 
    (<parameter_list> 
    value IN int) 
    RETURN int IS 
BEGIN 
    [...other statements...] 
    update table1 set col1 = col1 - value where id in (<parameter_list>) 

    RETURN SQL%ROWCOUNT ; 
END; 

Además, me gustaría llamar a este procedimiento desde C#, por lo que debe ser compatible con las capacidades de .NET.

Gracias, Robert

Respuesta

25

El uso de CSV es probablemente la forma más sencilla, suponiendo que puede estar 100% seguro de que sus elementos no contendrán cadenas.

Una manera alternativa, y probablemente más robusta, de hacerlo es crear un tipo personalizado como una tabla de cadenas. Suponiendo sus cuerdas nunca fueron más de 100 caracteres, entonces usted podría tener:

CREATE TYPE string_table AS TABLE OF varchar2(100); 

Luego, puede pasar una variable de este tipo en su procedimiento almacenado y hacer referencia a ella directamente. En su caso, algo como esto:

FUNCTION EXECUTE_UPDATE(
    identifierList string_table, 
    value int) 
RETURN int 
IS 
BEGIN 

    [...other stuff...] 

    update table1 set col1 = col1 - value 
    where id in (select column_value from table(identifierList)); 

    RETURN SQL%ROWCOUNT; 

END 

La función table() convierte su tipo personalizado en una tabla con una sola columna "column_value", que luego se puede tratar como cualquier otra tabla (también lo hacen une o, en este caso, subselecciones).

La belleza de esto es que Oracle creará un constructor para ti, así que cuando se llama a su procedimiento almacenado simplemente hay que escribir:

execute_update(string_table('foo','bar','baz'), 32); 

Estoy asumiendo que usted puede manejar la construcción de este comando desde programáticamente DO#.

Como nota aparte, en mi empresa tenemos varios de estos tipos personalizados definidos como estándar para listas de cadenas, dobles, ints, etc. También utilizamos Oracle JPublisher para poder mapear directamente desde estos tipos en objetos Java correspondientes. Eché un vistazo rápido pero no pude ver ningún equivalente directo para C#. Solo pensé que lo mencionaría en caso de que los desarrolladores de Java encuentren esta pregunta.

+2

Nota rápida: CSV no funcionará por sí solo, ya que Oracle lo tratará como una sola cadena. – sarsnake

+0

¿No debería ser '... de string_table (identifierList)); ... '? –

-1

que no lo han hecho para Oracle, pero con SQL Server, puede utilizar una función para convertir una cadena CSV en una tabla, que puede ser utilizado en una cláusula IN. Debe ser recta hacia adelante para volver a escribir esto para Oracle (creo!)

CREATE Function dbo.CsvToInt (@Array varchar(1000)) 
returns @IntTable table 
    (IntValue nvarchar(100)) 
AS 
begin 

    declare @separator char(1) 
    set @separator = ',' 

    declare @separator_position int 
    declare @array_value varchar(1000) 

    set @array = @array + ',' 

    while patindex('%,%' , @array) <> 0 
    begin 

     select @separator_position = patindex('%,%' , @array) 
     select @array_value = left(@array, @separator_position - 1) 

     Insert @IntTable 
     Values (Cast(@array_value as nvarchar)) 

     select @array = stuff(@array, 1, @separator_position, '') 
    end 

    return 
end 

entonces puede pasar en una cadena CSV (por ejemplo, '0001,0002,0003') y hacer algo como

UPDATE table1 SET 
     col1 = col1 - value 
WHERE id in (SELECT * FROM csvToInt(@myParam)) 
+0

Algo similar se puede hacer con PL/SQL pero es más rápido pasar una colección al procedimiento como una cadena csv. La cadena csv necesita ser tokenizada primero en PL/SQL. – tuinstoel

-1

¿Por qué tiene que ser un procedimiento almacenado? Podría construir una declaración preparada programáticamente.

+1

necesito ejecutar en el procedimiento plsql más declaraciones que una "actualización". Simplifiqué un poco el problema por el bien del ejemplo. –

0

Hay un artículo en el sitio web AskTom que muestra cómo crear una función para analizar la cadena CSV y usar esto en su extracto de una manera similar al ejemplo de SQL Server dado.

Ver Asktom

1

creo que no hay manera directa para crear procedimientos con número variable de parámetros. Sin embargo, hay algunas soluciones, al menos parciales al problema, descritas en here.

  1. Si hay algunos tipos de llamadas típicos, la sobrecarga de procedimientos puede ayudar.
  2. Si hay un límite superior en el número de parámetros (y su tipo también se conoce de antemano), los valores predeterminados de los parámetros pueden ayudar.
  3. La mejor opción es tal vez el uso de variables de cursor que son punteros a los cursores de la base de datos.

Lamentablemente no tengo experiencia con entornos .NET.

+0

"no hay forma directa de crear procedimientos con un número variable de parámetros" ... Sí, sí, use CREATE TYPE xxx AS TABLE OF yyy y luego use ese tipo como el tipo de parámetro. – ObiWanKenobi

+0

No, no es una solución directa. De todos modos, su método es la respuesta aceptada que data de 2008. – rics

0

¿Por qué no utilizar una larga lista de parámetros y cargar los valores en un constructor de tablas? Aquí está el SQL/PSM para este truco

UPDATE Foobar 
    SET x = 42 
WHERE Foobar.keycol 
     IN (SELECT X.parm 
      FROM (VALUES (in_p01), (in_p02), .., (in_p99)) X(parm) 
      WHERE X.parm IS NOT NULL); 
2

me encontré con el siguiente artículo de Mark A. Williams, que pensé que sería una adición útil a este hilo. El artículo da un buen ejemplo de pasar matrices de C# para PL procs/SQL utilizando matrices asociativas (TIPO myType ES TABLA DE mytable.row% Tipo de índice POR PLS_INTEGER):

Great article by Mark A. Williams

Cuestiones relacionadas