use*_*353 18 performance sql-server-2008
我有一个参数为 NULL 作为默认值的 SP,然后我想做这样的查询:
SELECT ...
FROM ...
WHERE a.Blah = @Blah AND (a.VersionId = @VersionId OR (@VersionId IS NULL AND a.VersionId IS NULL));
Run Code Online (Sandbox Code Playgroud)
在WHERE
上述检查两个非空值和一个NULL值@VersionId
。
在性能方面,改为使用IF
语句并将查询复制到一个搜索非 NULL 的查询和另一个搜索 NULL 的查询中会不会更好?:
IF @VersionId IS NULL BEGIN
SELECT ...
FROM ...
WHERE a.Blah = @Blah AND a.VersionId IS NULL;
ELSE BEGIN
SELECT ...
FROM ...
WHERE a.Blah = @Blah AND a.VersionId = @VersionId;
END
Run Code Online (Sandbox Code Playgroud)
或者查询优化器使它本质上是一样的?
更新:
(注意:我使用的是 SQL Server)
(据我所知,a.VersionId = @VersionId
这两种情况都不能使用,是吗?)
And*_*y M 37
这个图案
column = @argument OR (@argument IS NULL AND column IS NULL)
Run Code Online (Sandbox Code Playgroud)
可以替换为
EXISTS (SELECT column INTERSECT SELECT @argument)
Run Code Online (Sandbox Code Playgroud)
这将使您将 NULL 与 NULL 匹配,并允许引擎有效地使用索引column
。要对该技术进行深入的深入分析,请参阅 Paul White 的博客文章:
由于在您的特定情况下有两个参数,您可以使用相同的匹配技术@Blah
- 这样您就可以或多或少地简洁地重写整个 WHERE 子句:
WHERE
EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)
Run Code Online (Sandbox Code Playgroud)
这将使用 上的索引快速工作(a.Blah, a.VersionId)
。
或者查询优化器使它本质上是一样的?
在这种情况下,是的。在 SQL Server 2005 以后的所有版本(至少)中,优化器可以识别模式col = @var OR (@var IS NULL AND col IS NULL)
并将其替换为适当的IS
比较。这确实依赖于内部重写匹配,因此可能存在更复杂的情况,这并不总是可靠的。
在SQL Server版本从2008 SP1 CU5包容性,也有使用的选项参数中嵌入优化通过OPTION (RECOMPILE)
,其中的任何参数或变量的运行值嵌入查询作为编译前一个文字。
因此,至少在很大程度上,在这种情况下,选择是风格问题,尽管INTERSECT
结构无可否认地紧凑而优雅。
以下示例显示了每个变体的“相同”执行计划(不包括文字与变量引用):
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);
Run Code Online (Sandbox Code Playgroud)