2012-07-19 21 views
5

Tengo una consulta en la que deseo devolver todas las filas que están asociadas a una lista de valores. Se puede escribir este mismo simplemente como:Filtrado de consulta SQL por lista de parámetros

select * from TableA where ColumnB in (1, 2, 3, 5) 

que podía generar esta consulta en C# y ejecutarlo. Sin embargo, esto es obviamente menos que ideal, ya que no utiliza parámetros, sufrirá al tratar de caché planes de consulta y es obviamente vulnerable a un ataque de inyección SQL.

Una alternativa es escribir esto como:

select * from TableA where ColumnB = @value 

Esto podría ser ejecutado muchas veces por C#, sin embargo, esto resultará en N DB golpea.

La única otra alternativa que puedo ver es crear una tabla temporal y unirme de esa manera, sin embargo, no veo este punto porque sería más complejo y sufriría las mismas limitaciones que la primera opción.

Estoy usando SQL Server y OLDB, creando la consulta no es el problema. Estoy tratando de crear el proceso más eficiente.

¿Cuál de estos tres métodos es más eficiente? ¿Me he perdido una alternativa?

+0

¿cómo quieres ejecutar la consulta? EF, LINQ, ADO, OLEDB? – paul

+0

¿Y qué servidor? MySql, MsSql, otro? – mmdemirbas

+0

OLDB y MsSQL, pregunta actualizada – Liath

Respuesta

4

Suponiendo SQL Server 2008 o posterior, en SQL Server, crear un tipo de tabla una vez:

CREATE TYPE dbo.ColumnBValues AS TABLE 
(
    ColumnB INT 
); 

A continuación, un procedimiento almacenado que toma un tipo tales como entrada:

CREATE PROCEDURE dbo.whatever 
    @ColumnBValues dbo.ColumnBValues READONLY 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SELECT A.* FROM dbo.TableA AS A 
    INNER JOIN @ColumnBValues AS c 
    ON A.ColumnB = c.ColumnB; 
END 
GO 

Ahora en C# , cree una DataTable y pase eso como un parámetro para el procedimiento almacenado:

DataTable cbv = new DataTable(); 
cbv.Columns.Add(new DataColumn("ColumnB")); 

// in a loop from a collection, presumably: 
cbv.Rows.Add(someThing.someValue); 

using (connectionObject) 
{ 
    SqlCommand cmd  = new SqlCommand("dbo.whatever", connectionObject); 
    cmd.CommandType  = CommandType.StoredProcedure; 
    SqlParameter cbvParam = cmd.Parameters.AddWithValue("@ColumnBValues", cbv); 
    cbvParam.SqlDbType = SqlDbType.Structured; 
    //cmd.Execute...; 
} 

(Es posible que desee m Como el tipo es mucho más genérico, lo llamé específicamente para dejar en claro lo que está haciendo.)

0

Usted puede escribir esto:

String csvString = "1, 2, 3, 5"; // Built the list somehow, don't forget escaping 
String query = "select * from TableA where ColumnB in (" + csvString + ")"; 

De esta manera, el rendimiento no disminuye, y se puede prevenir inyección valores de entrada simplemente se escapan SQL al crear csvString.

Por cierto, si se utiliza MS SQL en lugar de SQL estándar, puede findalternativeways.

+0

Sí, así es como lo estoy haciendo actualmente. Mi problema es que esto creará un nuevo Plan de ejecución cada vez que se ejecute esta consulta, ya que el comando es diferente y, por lo tanto, ralentizará considerablemente el rendimiento ... – Liath

+1

@Liath puede evitar algo de esto utilizando ['optimize for ad hoc workloads'] (configuración de http://msdn.microsoft.com/en-us/library/cc645587.aspx). De esta forma, los planes no se almacenan en caché hasta que se haya ejecutado una consulta específica dos veces. Su consulta seguirá generando un escaneo, pero no ocupará espacio en la memoria caché de su plan a menos que sea necesario (por ejemplo, si se vuelve a utilizar). –

2

También puede utilizar multiple resultsets y enviar un bounch de consulta como esta:

select * from TableA where ColumnB = @value0 
select * from TableA where ColumnB = @value1 
select * from TableA where ColumnB = @value2 
... 
select * from TableA where ColumnB = @valuen 

en una sola llamada. aunque aparentemente sea contrario a la intuición, aprovecha el plan de ejecución y es seguro en términos de parametrización.

+0

Tengo curiosidad sobre el -1 Recibí –

+0

¿No es esto exactamente lo que OP dijo que no querían hacer? –

+0

@ AaronFertrand no exactamente: quiere usar el plan de ejecución, y esto lo hará. Tenga en cuenta que no están separados de ida y vuelta, pero todas las consultas se ejecutan en una sola ronda al DB. Como ventaja, la consulta está correctamente parametrizada para evitar la inyección. –

Cuestiones relacionadas