[英]Optional parameter in SQL query very slow when checking for NULL
我有许多连接的表,并且最大行数约为 400 万条记录。 我们正在存储过程中搜索此表,并且有一个默认值为 NULL 的可选参数,下面是我们正在运行的编辑示例,连接中涉及更多表,但只有 1 个字段具有 WHERE 子句;
DECLARE @OwnerId VARCHAR (50)=NULL
SET @OwnerId = 'A123456'
SELECT DISTINCT
t1.Id,
t2.OwnerId,
FROM
table1 t1
INNER JOIN [table2] t2 m ON t1.Id = t2.id
WHERE
t2.OwnerId = @OwnerId
OwnerId 上有一个索引。 运行上述查询在不到 1 秒的时间内返回结果 (600)。 但是,正如所指出的,该参数是可选的(以及其他参数),只要我稍微更改它以包含该值(如果不是 NULL)(我相信这是这样做的),相同的搜索就会超过 10 秒。
SET @OwnerId = 'A123456'
SELECT DISTINCT
t1.Id,
t2.OwnerId,
FROM
table1 t1
INNER JOIN [table2] t2 ON t1.Id = t2.id
WHERE
(@OwnerId IS null OR (t2.OwnerId = @OwnerId))
我无法在生产服务器上运行执行计划,但在开发服务器上运行时可以看到略有不同(这没有 400 万行),所以有些事情正在发生变化,但不确定是什么。
像这样的查询可能会受到查询计划缓存的极大影响。 当您有一个带有像WHERE Column = @Param OR @Param IS NULL
这样的子句的 SP 时,SQL Server 将在第一次运行 SP 时缓存该计划。 如果@Param
有一个非NULL
值,那么这个计划可能是可怕的,当它的值为NULL
,作为行估计会比它实际上将返回严重降低。
此外,具有非NULL
值的计划可能会涉及索引查找(我希望您有良好的索引),而跨整个表的查找对于性能来说将非常差(可能比扫描更差)。
对于像这样简单的事情,您可能只需添加OPTION (RECOMPILE)
就可以“逃脱”; 这将强制 DBMS 在您每次运行查询时重新创建计划。 但是,这确实有开销,对于复杂的查询,您可能应该使用动态 SQL 来生成动态WHERE
子句:
SELECT DISTINCT
t1.Id,
t2.OwnerId,
FROM
table1 t1
INNER JOIN [table2] t2 ON t1.Id = t2.id
WHERE
(@OwnerId IS null OR (t2.OwnerId = @OwnerId))
OPTION (RECOMPILE);
DISTINCT
,在一个有 400 万行的表上,并要求每一行,无论如何都会有问题; 因为这对服务器来说是很多开销,因为它需要对所有行进行排序。 如果您在t1.Id
和t2.OwnerId
上有索引,那么这将有助于 RDBMS; 但如果你在这么大的数据集上做一个DISTINCT
,在我看来,这确实推断出设计缺陷。
Gail Shaw 和 Aaron Bertrand 也分别写了一些关于他们称之为Catch-All Queries和Kitchen Sink Queries 的文章,这些文章可能值得一读。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.