繁体   English   中英

检查 NULL 时,SQL 查询中的可选参数非常慢

[英]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.Idt2.OwnerId上有索引,那么这将有助于 RDBMS; 但如果你在这么大的数据集上做一个DISTINCT ,在我看来,这确实推断出设计缺陷。

Gail Shaw 和 Aaron Bertrand 也分别写了一些关于他们称之为Catch-All QueriesKitchen Sink Queries 的文章,这些文章可能值得一读。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM