2009-10-26 26 views
7

Tengo una aplicación C# .net que necesito modificar. La consulta en el momento efectivamente hace esto:Parámetros de Oracle con instrucción IN?

select * from contract where contractnum = :ContractNum 

(muy simplificada, sólo para demostrar que estamos utilizando un = y un parámetro adicional)

Ese parámetro se lee desde el archivo en el Settings.Settings Aplicación C# y tiene una cadena en ella. Necesito modificarlo para incluir múltiples contratos, por lo que figura que puedo cambiar el SQL para:

select * from contract where contractnum in (:ContractNum) 

pero que no devuelve ningún resultado, no importa cómo puedo dar formato a la cadena en el parámetro.

¿Hay alguna manera de hacer que Oracle haga una IN con un parámetro?

cualquier ayuda apreciada, gracias a todos.

+0

Cuando usa odp.net o devart como proveedor de datos, puede usar una colección de Oracle (una tabla anidada) como parámetro. Esta es la forma más rápida, pero no es posible cuando usa system.data.oracleclient. Entonces, ¿qué tipo de proveedor de datos usas? – tuinstoel

Respuesta

2

Aún no hemos encontrado un archivo db que permita evaluar una sola variable de cadena que contenga comas para separarla como la única cláusula IN.

Sus opciones son subcadenas de la variable para que el contenido de la variable delimitada por comas se convierta en filas, por lo que puede unirse a esto. O para usar SQL dinámico, que es una declaración SQL construida como una cadena en un sproc antes de que se ejecute la instrucción.

+0

he encontrado una referencia a la utilización y en lugar de: para la identificación de parámetros y que funciona: valor parámetro: '1182411', '1182423' SQL: seleccionar * de contrato en el que contractnum en (y ContractNum) no tengo idea por qué esto funciona, o si es "oficialmente" compatible con Orace o simplemente con TOAD. ¿Ya usaste & para Oracle? ¿Alguna idea de la diferencia? – Gareth

+0

: la variable es una variable de vinculación; la única vez que recuerdo usar & variable fue definir/usar una variable de enlace dentro de PLSQL Developer. –

+2

El signo & es el carácter predeterminado que indica una variable de sustitución en SQL * Plus. TOAD (y otros IDEs) admiten alguna sintaxis de SQL * Plus. – APC

6

puede usar una función de canalización para transformar una cadena en una tabla que podría usarse con el operador IN. Por ejemplo (probado con 10gR2):

SQL> select * from table(demo_pkg.string_to_tab('i,j,k')); 

COLUMN_VALUE 
----------------- 
i 
j 
k 

con el siguiente paquete:

SQL> CREATE OR REPLACE PACKAGE demo_pkg IS 
    2  TYPE varchar_tab IS TABLE OF VARCHAR2(4000); 
    3  FUNCTION string_to_tab(p_string VARCHAR2, 
    4       p_delimiter VARCHAR2 DEFAULT ',') 
    5  RETURN varchar_tab PIPELINED; 
    6 END demo_pkg; 
    7/

Package created 
SQL> CREATE OR REPLACE PACKAGE BODY demo_pkg IS 
    2  FUNCTION string_to_tab(p_string VARCHAR2, 
    3       p_delimiter VARCHAR2 DEFAULT ',') 
    4  RETURN varchar_tab PIPELINED IS 
    5  l_string   VARCHAR2(4000) := p_string; 
    6  l_first_delimiter NUMBER := instr(p_string, p_delimiter); 
    7  BEGIN 
    8  LOOP 
    9   IF nvl(l_first_delimiter,0) = 0 THEN 
10    PIPE ROW(l_string); 
11    RETURN; 
12   END IF; 
13   PIPE ROW(substr(l_string, 1, l_first_delimiter - 1)); 
14   l_string   := substr(l_string, l_first_delimiter + 1); 
15   l_first_delimiter := instr(l_string, p_delimiter); 
16  END LOOP; 
17  END; 
18 END demo_pkg; 
19/

Package body created 

Su consulta se vería así:

select * 
    from contract 
where contractnum in (select column_value 
         from table(demo_pkg.string_to_tab(:ContractNum))) 
+1

+1 - AFAIK, esta es la única forma de usar todos los siguientes: vincular variable, cantidad desconocida de elementos y la cláusula "IN". Si tiene un límite superior conocido en el número de elementos, siempre puede codificar la sentencia para usar ese número de elementos y sustituir nulas de manera programática cuando haya marcadores de posición sobrantes – dpbradley

+0

No, esta no es la única forma de usar variables de vinculación. También puede vincular una colección de números de Oracle y unirse a la tabla (: números). Ya no necesitará una función canalizada. Sin embargo, su proveedor de datos tiene que ser compatible. – tuinstoel

+0

Este es el enfoque que hemos estado utilizando durante casi 10 años. Después de (finalmente) pasar a utilizar ODP.NET, nos vemos obligados a utilizar el método destacado en la respuesta publicada por tuinstoel. Esa parece ser una solución mucho más agradable de todos modos, en lugar de dividir las cuerdas. – Carl

6

Se puede utilizar una colección de Oracle de numeración un parámetro (variable de vinculación) cuando utiliza ODP.NET como proveedor de datos. Esto funciona con Oracle server 9, 10 u 11 y ODP.net release> = 11.1.0.6.20.

Una solución similar es posible cuando utiliza el proveedor de datos .NET de Devart para Oracle.

Vamos a seleccionar los contratos con contractnum de 3 y 4.

Tenemos que utilizar un tipo de Oracle para transferir un conjunto de números de contrato a nuestra consulta.

MDSYS.SDO_ELEM_INFO_ARRAY se usa porque si usamos este tipo de Oracle ya predefinido no tenemos que definir nuestro propio tipo de Oracle. Puede completar MDSYS.SDO_ELEM_INFO_ARRAY con un máximo de 1048576 números.

using Oracle.DataAccess.Client; 
using Oracle.DataAccess.Types; 

[OracleCustomTypeMappingAttribute("MDSYS.SDO_ELEM_INFO_ARRAY")] 
public class NumberArrayFactory : IOracleArrayTypeFactory 
{ 
    public Array CreateArray(int numElems) 
    { 
    return new Decimal[numElems]; 
    } 

    public Array CreateStatusArray(int numElems) 
    { 
    return null; 
    } 
} 

private void Test() 
{ 
    OracleConnectionStringBuilder b = new OracleConnectionStringBuilder(); 
    b.UserID = "sna"; 
    b.Password = "sna"; 
    b.DataSource = "ora11"; 
    using (OracleConnection conn = new OracleConnection(b.ToString())) 
    { 
    conn.Open(); 
    using (OracleCommand comm = conn.CreateCommand()) 
    { 
     comm.CommandText = 
     @" select /*+ cardinality(tab 10) */ c.* " + 
     @" from contract c, table(:1) tab " + 
     @" where c.contractnum = tab.column_value"; 

     OracleParameter p = new OracleParameter(); 
     p.OracleDbType = OracleDbType.Array; 
     p.Direction = ParameterDirection.Input; 
     p.UdtTypeName = "MDSYS.SDO_ELEM_INFO_ARRAY"; 
     //select contract 3 and 4 
     p.Value = new Decimal[] { 3, 4 }; 
     comm.Parameters.Add(p); 

     int numContracts = 0; 
     using (OracleDataReader reader = comm.ExecuteReader()) 
     { 
     while (reader.Read()) 
     { 
      numContracts++; 
     } 
     } 
     conn.Close(); 
    } 
    } 
} 

El índice en contract.contractnum no se utiliza cuando se omite pista/* + cardinalidad (lengüeta 10) * /. Supuse que contractnum es la clave principal, por lo que esta columna se indexará.

Ver también aquí: http://forums.oracle.com/forums/thread.jspa?messageID=3869879#3869879

+0

Lea también: http://blog.tanelpoder.com/2012/08/02/the-limitations-of-cursor_sharing-force-and-force_matching_signature-for-sql-plan-stability/ – TTT

+0

Esto funcionó para nuestro escenario. El problema fue incluir NumberArrayFactory, aunque no se usa explícitamente en el código. – Carl

0

Para poder utilizar parámetros con EN declaración puede utilizar esta construcción:

select * from contract where contractnum 
in (select column_value from table (:ContractNum)) 

donde ContractNum es el tipo de matriz personalizada.

Cuestiones relacionadas