2012-07-19 4 views
5

J'ai une requête où je veux retourner toutes les lignes qui sont associées à une liste de valeurs. Vous pouvez écrire cela très simplement:Filtrage des requêtes SQL par liste de paramètres

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

Je pourrais générer cette requête en C# et l'exécuter. Cependant, ceci n'est évidemment pas idéal car il n'utilise pas de paramètres, il souffrira en essayant de mettre en cache des plans de requête et est évidemment vulnérable à une attaque par injection SQL.

Une alternative est d'écrire cela comme:

select * from TableA where ColumnB = @value 

Cela pourrait être exécuté plusieurs fois par C#, mais cela se traduira par DB N frappe. La seule autre alternative que je peux voir est de créer une table temporaire et de la joindre de cette façon, mais je ne vois pas ce point car cela serait plus complexe et souffrirait des mêmes limitations que la première option. J'utilise SQL Server et OLDB, la création de la requête n'est pas le problème. J'essaie de créer le processus le plus efficace.

Laquelle de ces trois méthodes est la plus efficace? Ai-je manqué une alternative?

+0

Comment voulez-vous exécuter la requête? EF, LINQ, ADO, OLEDB? – paul

+0

Et quel serveur? MySql, MsSql, autre? – mmdemirbas

+0

OLDB et MsSQL, question mise à jour – Liath

Répondre

4

En supposant SQL Server 2008 ou plus récent, dans SQL Server, créez un type de table une fois:

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

Ensuite, une procédure stockée qui prend un tel type en entrée:

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 

maintenant en C# , créez un DataTable et transmettez-le en tant que paramètre à la procédure stockée:

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...; 
} 

(Vous souhaiterez peut-être m Comme le type est beaucoup plus générique, je l'ai nommé spécifiquement pour clarifier ce qu'il fait.)

0

Vous pouvez facilement écrire ceci:

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

De cette manière, la performance n'a pas diminué, et vous pouvez empêcher Sql Injection valeurs d'entrée simplement s'échapper tout en créant csvString.

BTW, si vous utilisez MS SQL au lieu de la norme SQL, vous pouvez findalternativeways.

+0

Oui, c'est comme ça que je le fais actuellement. Mon problème est que cela créera un nouveau plan d'exécution chaque fois que cette requête s'exécutera car la commande est différente, ce qui ralentira considérablement les performances ... – Liath

+1

@Liath vous pouvez éviter cela en utilisant ['optimiser pour les charges de travail ad hoc '] (http://msdn.microsoft.com/en-us/library/cc645587.aspx) paramètre. De cette façon, les plans ne sont pas mis en cache tant qu'une requête spécifique n'a pas été exécutée deux fois. Votre requête va toujours générer une analyse, mais elle ne prendra pas de place dans votre cache de plan à moins qu'elle ne le devienne (par exemple, elle est réellement réutilisée). –

2

Vous pouvez également utiliser multiple resultsets et envoyer un bounch de requête comme ceci:

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

dans un seul appel . même si apparemment contre-intuitif, il exploite le plan d'exécution et est sûr en termes de paramétrage.

+0

Je suis curieux de connaître le -1 J'ai reçu –

+0

N'est-ce pas exactement ce que le PO a dit qu'ils ne voulaient pas faire? –

+0

@AaronBertrand pas exactement: il veut utiliser le plan d'exécution, et ce sera le cas. Notez qu'ils ne sont pas séparés roundtrip, mais toutes les requêtes sont exécutées dans un seul rountrip à la base de données. En plus, la requête est correctement paramétrée, pour éviter l'injection. –