2012-07-19 15 views
5

Ich habe eine Abfrage, wo ich alle Zeilen, die mit einer Liste von Werten verbunden sind, zurückgeben möchte. Sie könnten dies sehr einfach schreiben als:SQL Abfrage Filterung nach Liste der Parameter

Ich könnte diese Abfrage in C# generieren und ausführen. Dies ist jedoch offensichtlich weniger als ideal, da es keine Parameter verwendet, es wird leiden, wenn versucht wird, Abfragepläne zwischenzuspeichern, und ist offensichtlich anfällig für einen SQL-Injection-Angriff.

Eine Alternative ist, zu schreiben wie:

select * from TableA where ColumnB = @value 

Dieses von C# viele Male ausgeführt werden konnte, was jedoch in N DB führen trifft. Die einzige andere Alternative, die ich sehen kann, besteht darin, eine temporäre Tabelle zu erstellen und ihr auf diese Weise beizutreten. Allerdings sehe ich diesen Punkt nicht, da dieser komplexer wäre und unter den gleichen Einschränkungen leidet wie die erste Option.

Ich benutze SQL Server und OLDB, Erstellen der Abfrage ist nicht das Problem. Ich versuche den effizientesten Prozess zu erstellen.

Welche dieser drei Methoden ist effizienter? Habe ich eine Alternative verpasst?

+0

Wie möchten Sie die Abfrage ausführen? EF, LINQ, ADO, OLEDB? – paul

+0

Und welcher Server? MySql, MsSql, andere? – mmdemirbas

+0

OLDB und MsSQL, Frage aktualisiert – Liath

Antwort

4

Unter der Annahme, SQL Server 2008 oder höher, in SQL Server, einen Tabellentyp erstellen, sobald:

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

Dann eine gespeicherte Prozedur, die eine solche Art als Eingabe:

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 

Jetzt in C# schaffen, eine Datentabelle und übergeben, der als Parameter an die gespeicherte Prozedur:

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

(Sie zu m wollen könnte Ake der Typ viel generischer, nannte ich es speziell, um es klar zu machen, was es tut.)

0

du leicht schreiben kann:

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

Durch diese Weise wird die Leistung nicht verringert, und Sie können SQL-Injection einfach entkommen Eingabewerte verhindern, während csvString zu schaffen.

BTW, wenn Sie verwenden MS SQL anstelle von Standard-SQL, können Sie findalternativeways.

+0

Ja, so mache ich es gerade. Mein Problem ist, dass dies jedes Mal, wenn diese Abfrage ausgeführt wird, einen neuen Ausführungsplan erstellt, da der Befehl unterschiedlich ist und dadurch die Leistung erheblich verlangsamt ... – Liath

+1

@Liath Sie können dies vermeiden, indem Sie [für Ad-hoc-Workloads optimieren] verwenden (http://msdn.microsoft.com/en-us/library/cc645587.aspx) Einstellung. Auf diese Weise werden Pläne nicht zwischengespeichert, bis eine bestimmte Abfrage zweimal ausgeführt wurde. Ihre Abfrage wird immer noch einen Scan ergeben, aber sie wird keinen Platz in Ihrem Plancache einnehmen, es sei denn, sie sollte (z. B. wird sie wirklich wiederverwendet). –

2

Sie können auch multiple resultsets verwenden und eine bounch Abfrage wie folgt senden:

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

in einem einzigen Anruf. Auch wenn es offensichtlich nicht intuitiv ist, nutzt es den Ausführungsplan und ist hinsichtlich der Parametrisierung sicher.

+0

Ich bin neugierig auf die -1 Ich erhielt –

+0

Ist das nicht genau das, was das OP sagte, sie wollten nicht tun? –

+0

@AaronBertrand nicht genau: Er möchte den Ausführungsplan verwenden, und das wird. Beachten Sie, dass sie nicht Roundtrip-getrennt sind, sondern alle Abfragen in einem einzigen Rountrip zur DB ausgeführt werden. Als ein Plus ist die Abfrage richtig parametriert, um eine Injektion zu vermeiden. –