Haftungsausschluss : Einige der Dinge in dieser Antwort können einen DBA zusammenzucken lassen. Ich gehe es von einem reinen Performance-Standpunkt aus an - wie man Index-Suchanfragen erhält, wenn man immer Index-Scans erhält.
Damit geht es aus dem Weg.
Ihre Abfrage wird als "Abfrage für Küchenspülen" bezeichnet - eine einzelne Abfrage, die eine Reihe möglicher Suchbedingungen abdeckt. Wenn der Benutzer @status
einen Wert festlegt , möchten Sie nach diesem Status filtern. Wenn dies der Fall @status
ist NULL
, geben Sie alle Status usw. zurück.
Dies führt zu Problemen bei der Indizierung, die jedoch nicht mit der Sargabilität zusammenhängen, da alle Suchbedingungen den Kriterien entsprechen.
Das ist sargable:
WHERE [status]=@status
Dies ist nicht sargable, da SQL Server ISNULL([status], 0)
für jede Zeile auswerten muss, anstatt einen einzelnen Wert im Index nachzuschlagen :
WHERE ISNULL([status], 0)=@status
Ich habe das Problem mit dem Spülbecken in einer einfacheren Form nachgebildet:
CREATE TABLE #work (
A int NOT NULL,
B int NOT NULL
);
CREATE UNIQUE INDEX #work_ix1 ON #work (A, B);
INSERT INTO #work (A, B)
VALUES (1, 1), (2, 1),
(3, 1), (4, 1),
(5, 2), (6, 2),
(7, 2), (8, 3),
(9, 3), (10, 3);
Wenn Sie Folgendes versuchen, erhalten Sie einen Index-Scan, obwohl A die erste Spalte des Index ist:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Dies führt jedoch zu einer Indexsuche:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Solange Sie eine überschaubare Anzahl von Parametern verwenden (in Ihrem Fall zwei), könnten Sie wahrscheinlich nur UNION
eine Reihe von Suchabfragen durchführen - im Grunde alle Permutationen von Suchkriterien. Wenn Sie drei Kriterien haben, sieht dies chaotisch aus, bei vier ist es völlig unüberschaubar. Du wurdest gewarnt.
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL
UNION ALL
SELECT *
FROM #work
WHERE @a=A AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b IS NULL;
Damit der dritte dieser vier einen Index-Suchvorgang verwenden kann, benötigen Sie jedoch einen zweiten Index (B, A)
. So könnte Ihre Abfrage mit diesen Änderungen aussehen (einschließlich meines Refactorings der Abfrage, um sie besser lesbar zu machen).
DECLARE @Status int = NULL,
@IsUserGotAnActiveDirectoryUser bit = NULL;
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='')
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='');
... und Sie benötigen einen zusätzlichen Index, Employee
wobei die beiden Indexspalten vertauscht sind.
Der Vollständigkeit halber sollte ich erwähnen, dass dies x=@x
implizit bedeutet, dass x
es nicht sein kann, NULL
weil NULL
es niemals gleich ist NULL
. Das vereinfacht die Abfrage etwas.
Und ja, Aaron Bertrands dynamische SQL-Antwort ist in den meisten Fällen die bessere Wahl (dh wann immer Sie mit den Neukompilierungen leben können).
@Status
?