2008-09-17 11 views
6

Trabajando con un SqlCommand en C# He creado una consulta que contiene una parte IN (lista ...) en la cláusula where. En lugar de recorrer mi lista de cadenas generando la lista que necesito para la consulta (peligroso si piensas en sqlInjection). Pensé que podía crear un parámetro como:Lista de cadenas en SqlCommand a través de los parámetros en C#

SELECT blahblahblah WHERE blahblahblah IN @LISTOFWORDS 

Luego, en el código intento agregar un parámetro de la siguiente manera:

DataTable dt = new DataTable(); 
dt.Columns.Add("word", typeof(string)); 
foreach (String word in listOfWords) 
{ 
    dt.Rows.Add(word); 
} 
comm.Parameters.Add("LISTOFWORDS", System.Data.SqlDbType.Structured).Value = dt; 

Pero esto no funciona.

Preguntas:

  • Estoy intentando algo imposible?
  • ¿Tomé el enfoque equivocado?
  • ¿Tengo errores en este enfoque?

Gracias por su tiempo :)

Respuesta

3

Lo que intenta hacer es posible, pero no está utilizando su enfoque actual. Este es un problema muy común con todas las soluciones posibles antes de que SQL Server 2008 tenga intercambios relacionados con el rendimiento, la seguridad y el uso de la memoria.

This link shows some approaches for SQL Server 2000/2005

SQL Server 2008 supports passing a table value parameter.

espero que esto ayude.

+4

El primer enlace apunta al mismo que el segundo – Matt

+0

Buenas soluciones :) – graffic

3

Usted quiere pensar en esa lista viene. En general, esa información está en la base de datos en alguna parte. Por ejemplo, en lugar de esto:

SELECT * FROM [Table] WHERE ID IN (1,2,3) 

se puede utilizar una subconsulta como esto:

SELECT * FROM [Table] WHERE ID IN (SELECT TableID FROM [OtherTable] WHERE OtherTableID= @OtherTableID) 
+0

Lamentablemente, la información no está en otra tabla. Es el resultado de una recopilación de datos. Pero es un buen enfoque que anotaré para usos futuros. Gracias – graffic

0

recomendaría establecer el parámetro como una cadena separada por comas de los valores y el uso de una función Split, en SQL para conviértalo en una tabla de valores de una sola columna y luego puede usar la función IN.

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - funciones de división

+0

Eso es bastante propenso a la inyección Sql :( – graffic

1

Si entiendo bien, están tratando de pasar una lista como un parámetro de SQL.

Algunas personas han intentado esto antes con un éxito limitado:

Passing Arrays to Stored Procedures

Arrays and Lists in SQL 2005

Passing Array of Values to SQL Server without String Manipulation

Using MS SQL 2005's XML capabilities to pass a list of values to a command

+0

El primer enlace habla de una variable de tabla. Esto es lo que he creado con DataTAble. Pero en este caso no puedo hacer que mssql se lo trague. Supongo que la idea es va a ser crear una tabla guid, word. Insertar allí y luego delte por guid – graffic

0

Si desea pasar la lista como una cadena en una parámetro, podría simplemente construir la consulta dinámicamente .

DECLARE @query varchar (500) SET @query = 'SELECT bla, bla, DONDE blahblah en (' + @List + ')' EXECUTE (@query)

+1

Bastante SQL Inyección propensa :(Pero gracias :) – graffic

0

que solía tener el mismo problema, Creo que ahora hay forma de hacerlo directamente sobre la API de ADO.NET.

Puede considerar insertar las palabras en una temptable (más un queryid o algo así) y luego hacer referencia a esa temptable de la consulta. O creando dinámicamente la cadena de consulta y evitando la inyección de sql por otras medidas (por ejemplo, verificaciones de expresiones regulares).

+0

Voy a ir con la primera opción + una inserción bulk sql. Tal vez esto me puede ahorrar algo de SQL E/S – graffic

1
  • ¿Estoy intentando algo imposible?

No, no es imposible.

  • ¿Tomé el enfoque equivocado?

Su enfoque no funciona (al menos en .net 2)

  • ¿Tengo errores en este enfoque?

Me gustaría probar la solución "Joel Coehoorn" (2 ª respuestas) si es posible. De lo contrario, otra opción es enviar un parámetro de "cadena" con todos los valores delimitados por un separador. Escribe una consulta dinámica (compila en función de los valores de la cadena) y ejecútala con "exec".

Otra solución será crear la consulta directamente desde el código. Algo como esto:

StringBuilder sb = new StringBuilder(); 
for (int i=0; i< listOfWords.Count; i++) 
{ 
    sb.AppendFormat("p{0},",i); 
    comm.Parameters.AddWithValue("p"+i.ToString(), listOfWords[i]); 
} 

comm.CommandText = string.Format(""SELECT blahblahblah WHERE blahblahblah IN ({0})", 
sb.ToString().TrimEnd(',')); 

El comando debe ser similar:

SELECT blah WHERE blah IN (p0,p1,p2,p3...)...p0='aaa',p1='bbb' 

En MSSQL2005, "IN" está trabajando únicamente con 256 valores.

+0

En la consulta inferior, aparece un error en la primera aparición de p0. – micahhoover

0

Esta es una vieja pregunta, pero he encontrado una solución elegante para esto que me encanta reutilizar y creo que todos los demás la encontrarán útil.

Antes que nada necesitas crear un FUNCTION en SqlServer que toma una entrada delimitada y devuelve una tabla con los elementos divididos en registros.

Aquí es el siguiente código para esto:

ALTER FUNCTION [dbo].[Split] 
(
    @RowData nvarchar(max), 
    @SplitOn nvarchar(5) = ',' 
) 
RETURNS @RtnValue table 
(
    Id int identity(1,1), 
    Data nvarchar(100) 
) 
AS 
BEGIN 
    Declare @Cnt int 
    Set @Cnt = 1 

    While (Charindex(@SplitOn,@RowData)>0) 
    Begin 
     Insert Into @RtnValue (data) 
     Select 
      Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1))) 

     Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData)) 
     Set @Cnt = @Cnt + 1 
    End 

    Insert Into @RtnValue (data) 
    Select Data = ltrim(rtrim(@RowData)) 

    Return 
END 

Ahora puede hacer algo como esto:

Select Id, Data from dbo.Split('123,234,345,456',',') 

Y no miedo, esto no puede ser susceptible a ataques de inyección SQL.

Siguiente escribir un procedimiento almacenado que toma los datos de su delimitado por comas y luego se puede escribir una instrucción SQL que utiliza esta función Split:

CREATE PROCEDURE [dbo].[findDuplicates] 
    @ids nvarchar(max) 
as 
begin 
    select ID 
     from SomeTable with (nolock) 
    where ID in (select Data from dbo.Split(@ids,',')) 
end 

Ahora se puede escribir un C# envoltorio alrededor de él:

public void SomeFunction(List<int> ids) 
{ 
    var idsAsDelimitedString = string.Join(",", ids.Select(id => id.ToString()).ToArray()); 

    // ... or however you make your connection 
    var con = GetConnection(); 

    try 
    { 
     con.Open(); 

     var cmd = new SqlCommand("findDuplicates", con); 

     cmd.Parameters.Add(new SqlParameter("@ids", idsAsDelimitedString)); 

     var reader = cmd.ExecuteReader(); 

     // .... do something here. 

    } 
    catch (Exception) 
    { 
     // catch an exception? 
    } 
    finally 
    { 
     con.Close(); 
    } 
} 
Cuestiones relacionadas