2009-11-10 15 views
6

Angenommen, Sie haben eine gespeicherte Prozedur und einen optionalen Parameter. Sie möchten diesen optionalen Parameter in der SQL-Abfrage verwenden. Normalerweise ist dies, wie ich habe es getan gesehen:Korrekte Möglichkeit, 'optionale' Where-Klausel-Filter in SQL zu behandeln?

SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = 'test' 
AND (@MyOptionalParam IS NULL OR t1.MyField = @MyOptionalParam) 

Diese gut zu funktionieren scheint, aber es verursacht eine hohe Menge an logischen liest, wenn Sie die Abfrage mit STATISTICS IO ON laufen. Ich habe auch die folgende Variante ausprobiert:

SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = 'test' 
AND t1.MyField = CASE WHEN @MyOptionalParam IS NULL THEN t1.MyField ELSE @MyOptionalParam END 

Und es ergibt die gleiche Anzahl von hohen liest. Wenn wir die SQL in eine Zeichenfolge konvertieren, dann rufen Sp_executesql darauf, die liest sind fast gleich Null:

DECLARE @sql nvarchar(max) 

SELECT @sql = 'SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = ''test''' 

IF @MyOptionalParam IS NOT NULL 
BEGIN 
    SELECT @sql = @sql + ' AND t1.MyField = @MyOptionalParam ' 
END 

EXECUTE sp_ExecuteSQL @sql, N'@MyOptionalParam', @MyOptionalParam 

Bin ich verrückt? Warum sind optionale Wo-Klauseln so schwer zu bekommen?

Update: Ich frage im Grunde, ob es eine Möglichkeit gibt, die Standardsyntax innerhalb einer gespeicherten Prozedur zu behalten und niedrige logische Lesevorgänge zu erhalten, wie die sp_ExecuteSql-Methode. Es scheint völlig verrückt zu sein, eine Zeichenkette aufzubauen ... ganz zu schweigen davon, dass es schwieriger zu warten, zu debuggen, zu visualisieren ist.

+0

Nicholas, siehe die Union-Ansatz unten für eine Möglichkeit zur Verwendung von Standard-SQL-Syntax ohne dynamische sql - Ich wäre sehr gespannt auf Sie sehen, wie es in Ihrem Szenario funktioniert ... – chadhoc

+0

@Nicholas: Erstellen einer Abfrage als Eine Zeichenkette vor der Ausführung ist ** genau ** was * dynamisch * SQL ist. Es ist ein kleines Problem zu debuggen - kopieren/einfügen, String-Verkettungssyntax loszuwerden. –

Antwort

1

Sie verwenden "OR" -Klausel (implizit und explizit) auf den ersten beiden SQL-Anweisungen. Der letzte ist ein "UND" -Kriterium. "ODER" ist immer teurer als "UND" -Kriterien. Nein, du bist nicht verrückt, sollte erwartet werden.

+2

'EXEC sp_executesql ** ** speichert den Abfrageplan seit v2005: http://www.sommarskog.se/dynamic_sql.html#queryplans –

+0

Sie haben Recht. Ich habe nicht bemerkt, dass er Parameter für sp_ExecuteSQL verwendet. – mevdiven

+0

Änderte meine Antwort entsprechend. Vielen Dank. – mevdiven

2

Dies ist eine weitere Variante der optionalen Parameter Technik:

SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = 'test' 
AND t1.MyField = COALESCE(@MyOptionalParam, t1.MyField) 

Ich bin ziemlich sicher, es wird jedoch die gleiche Leistung Problem haben. Wenn die Leistung # 1 ist, werden Sie wahrscheinlich mit der Verzweigung von Logik und in der Nähe von doppelten Abfragen oder dem Erstellen von Zeichenfolgen festhalten, was in TSQL ebenfalls schmerzhaft ist.

4

Wenn wir die SQL in eine Zeichenfolge konvertieren, dann Sp_executesql rufen Sie darauf das liest sich fast gleich Null ...

  1. Weil Ihre Abfrage nicht mehr eine OR-Auswertung, die, wie Sie kann Sargability sehen
  2. Der Abfrageplan wird zwischengespeichert, wenn sp_executesql verwendet wird; SQL Server muss nicht eine harte Parse tun ...

Excellent Ressource: The Curse & Blessing of Dynamic SQL

Solange Sie parametrisierte Abfragen verwenden, sollten Sie sicher vor SQL Injection attacks.

0

EDIT: Hinzufügen link to similar question/answer with context as to why the union/if...else approach works better than OR logic (FYI, Remus, die Antworter in dieser Verbindung, verwendeten auf dem SQL Server-Team arbeiten Service-Broker zu entwickeln und andere Technologien)

Veränderung gegenüber der mit der "oder" Syntax zu einer Union Ansatz Sie werden sehen, 2 sucht, dass Ihre logischen Lese so gering wie möglich halten zählen sollte:

SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = 'test' 
AND @MyOptionalParam IS NULL 
union all 
SELECT * FROM dbo.MyTableName t1 
WHERE t1.ThisField = 'test' 
AND t1.MyField = @MyOptionalParam 

Wenn Sie möchten, Deduplizierung die Ergebnisse, verwenden Sie eine „Vereinigung“ statt „Vereinigung aller“.

EDIT: Demo zeigt, dass der Optimierer ist intelligent genug, Scan mit einem Null-Variablenwert in UNION auszuschließen:

if object_id('tempdb..#data') > 0 
    drop table #data 
go 

-- Put in some data 
select top 1000000 
     cast(a.name as varchar(100)) as thisField, cast(newid() as varchar(50)) as myField 
into #data 
from sys.columns a 
cross join sys.columns b 
cross join sys.columns c; 
go 

-- Shwo count 
select count(*) from #data; 
go 

-- Index on thisField 
create clustered index ixc__blah__temp on #data (thisField); 
go 

set statistics io on; 
go 

-- Query with a null parameter value 
declare @MyOptionalParam varchar(50); 
select * 
from #data d 
where d.thisField = 'test' 
and  @MyOptionalParam is null; 
go 

-- Union query 
declare @MyOptionalParam varchar(50); 
select * 
from #data d 
where d.thisField = 'test' 
and  @MyOptionalParam is null 
union all 
select * 
from #data d 
where d.thisField = 'test' 
and  d.myField = '5D25E9F8-EA23-47EE-A954-9D290908EE3E'; 
go 

-- Union query with value 
declare @MyOptionalParam varchar(50); 
select @MyOptionalParam = '5D25E9F8-EA23-47EE-A954-9D290908EE3E' 
select * 
from #data d 
where d.thisField = 'test' 
and  @MyOptionalParam is null 
union all 
select * 
from #data d 
where d.thisField = 'test' 
and  d.myField = '5D25E9F8-EA23-47EE-A954-9D290908EE3E'; 
go 

if object_id('tempdb..#data') > 0 
    drop table #data 
go 
+0

Erste Abfrage liest ganze Tabelle. Dies ist keine gute Möglichkeit, IO zu minimieren. –

+0

Dies ist eine teurere Methode als die in der Frage dargestellte SQL-Anweisung. – mevdiven

+0

Sorry Leute, aber der Optimierer wird definitiv nicht die gesamte Tabelle in der ersten Abfrage scannen, es ist schlau genug, die Abfrage basierend auf einem Nullwert "UND" mit der Variablen auszuschließen. Ein einfaches Beispiel mit IO stat Ausgabe wird demonstrieren, run lokal und überprüfen Sie selbst (beachten Sie, wenn Sie nicht einen suchbaren Index auf ThisField haben, erhalten Sie immer einen Scan aufgrund der Abfrage dagegen, daher wird angenommen, dass) - Ich habe die Antwort mit dem Beispiel bearbeitet zeigen, - setzen in einigen Daten \t oben 1000000 \t \t Besetzung (a.name als VARCHAR (100)) auszuwählen, wie thisField, Guss (newid() als VARCHAR (50)), wie in myField \t #data von \t s – chadhoc

-1

Veränderung gegenüber der mit dem „oder“ Syntax zu einem zwei Abfrage Ansatz, werden Sie siehe 2 verschiedene Pläne, die Ihre logische Lesezählung so gering wie möglich halten sollen:

IF @MyOptionalParam is null 
BEGIN 

    SELECT * 
    FROM dbo.MyTableName t1 

END 
ELSE 
BEGIN 

    SELECT * 
    FROM dbo.MyTableName t1 
    WHERE t1.MyField = @MyOptionalParam 

END 

Sie benötigen Programmierer Drang zu kämpfen Duplikation hier zu reduzieren. Stellen Sie fest, dass Sie nach zwei grundlegend verschiedenen Ausführungsplänen fragen und zwei Abfragen benötigen, um zwei Pläne zu erstellen.

+0

Aber was ist, wenn Sie mehrere optionale Parameter haben, nach denen Sie filtern wollen? Ich denke, ich verstehe nicht, warum es "zwei grundlegend unterschiedliche Ausführungspläne" sind. Wenn ich ein Parser bin, schaue ich auf die Variable, geh "hey, es ist null und wird nie anders sein. Ich kann aufhören, darauf zu filtern." Aber ich nehme an, dass es zumindest in SQL 2005 nicht so funktioniert. –

+0

Wenn Sie mehrere optionale Parameter haben, sind die Chancen, dass nur ein paar für den Abfrageplan von Bedeutung sind ... verzweigen Sie einfach auf diese. Wie für Parameter Sniffing: http://sqlblog.com/blogs/ben_nevarez/archive/2009/08/27/the-query-optimizer-and-parameter-sniffing.aspx Viel Glück bei diesem Ansatz obwohl. –

+0

Zum Kommentarlosen Downvoter - ich verstehe. Sie glauben, dass Sie Recht haben und Sie sind auch in der Mehrheit. Sie liegen jedoch falsch. –

Verwandte Themen