Dieses Muster
column = @argument OR (@argument IS NULL AND column IS NULL)
kann durch ersetzt werden
EXISTS (SELECT column INTERSECT SELECT @argument)
Auf diese Weise können Sie eine NULL mit einer NULL abgleichen und die Engine kann einen Index column
effizient verwenden. Für eine hervorragende eingehende Analyse dieser Technik verweise ich Sie auf den Blog-Artikel von Paul White:
Da es in Ihrem speziellen Fall zwei Argumente gibt, können Sie dieselbe Matching-Technik verwenden, mit der @Blah
Sie die gesamte WHERE-Klausel mehr oder weniger präzise umschreiben können:
WHERE
EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)
Dies funktioniert schnell, wenn ein Index aktiviert ist (a.Blah, a.VersionId)
.
Oder das Abfrageoptimierungsprogramm macht es im Wesentlichen gleich?
In diesem Fall ja. In allen Versionen (mindestens) ab SQL Server 2005 kann das Optimierungsprogramm das Muster erkennen col = @var OR (@var IS NULL AND col IS NULL)
und durch den richtigen IS
Vergleich ersetzen . Dies hängt von der internen Anpassung des Überschreibens ab, sodass es komplexere Fälle geben kann, in denen dies nicht immer zuverlässig ist.
In Versionen von SQL Server ab einschließlich 2008 SP1 CU5 haben Sie auch die Möglichkeit, die Optimierung für das Einbetten von Parametern über zu verwenden OPTION (RECOMPILE)
, wobei der Laufzeitwert eines beliebigen Parameters oder einer Variablen vor dem Kompilieren als Literal in die Abfrage eingebettet wird.
Zumindest weitgehend ist die Wahl in diesem Fall eine Frage des Stils, obwohl die INTERSECT
Konstruktion unbestreitbar kompakt und elegant ist.
Die folgenden Beispiele zeigen den gleichen Ausführungsplan für jede Variation (Literale und variable Referenzen ausgeschlossen):
DECLARE @T AS table
(
c1 integer NULL,
c2 integer NULL,
c3 integer NULL
UNIQUE CLUSTERED (c1, c2)
);
-- Some data
INSERT @T
(c1, c2, c3)
SELECT 1, 1, 1 UNION ALL
SELECT 2, 2, 2 UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT 3, 3, 3;
-- Filtering conditions
DECLARE
@c1 integer,
@c2 integer;
SELECT
@c1 = NULL,
@c2 = NULL;
-- Writing the NULL-handling out explicitly
SELECT *
FROM @T AS T
WHERE
(
T.c1 = @c1
OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND
(
T.c2 = @c2
OR (@c2 IS NULL AND T.c2 IS NULL)
);
-- Using INTERSECT
SELECT *
FROM @T AS T
WHERE EXISTS
(
SELECT T.c1, T.c2
INTERSECT
SELECT @c1, @c2
);
-- Using separate queries
IF @c1 IS NULL AND @c2 IS NULL
SELECT *
FROM @T AS T
WHERE T.c1 IS NULL
AND T.c2 IS NULL
ELSE IF @c1 IS NULL
SELECT *
FROM @T AS T
WHERE T.c1 IS NULL
AND T.c2 = @c2
ELSE IF @c2 IS NULL
SELECT *
FROM @T AS T
WHERE T.c1 = @c1
AND T.c2 IS NULL
ELSE
SELECT *
FROM @T AS T
WHERE T.c1 = @c1
AND T.c2 = @c2;
-- Using OPTION (RECOMPILE)
-- Requires 2008 SP1 CU5 or later
SELECT *
FROM @T AS T
WHERE
(
T.c1 = @c1
OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND
(
T.c2 = @c2
OR (@c2 IS NULL AND T.c2 IS NULL)
)
OPTION (RECOMPILE);