为什么将查询计数分配给变量比直接检查它的效果更好



>最近我有一个性能调优的经验,我想在这里分享它,并试图理解为什么会发生这种改进。

在我的一个过程中,我想返回一个基于其他一些记录存在的数据集。

我的查询:

IF (SELECT COUNT(1) FROM ...) > 0
    SELECT …

此查询大约需要 5 秒。

我做了一个更改IF并将语句的输出分配给一个变量,然后检查它。

DECLARE @cnt INT = 0
SELECT @cnt = COUNT(1) FROM …
IF @cnt > 0
    SELECT …

这个运行时间不到 1 秒。

我也尝试了IF EXISTS,但在改进之前得到了相同的结果(5 秒)。

我非常想知道为什么编译器的行为如此不同,以及是否有任何特定的答案。

谢谢

这里有两部分。

1) SQL Server 优化器转换

IF (SELECT COUNT(1) FROM ...) > 0
    SELECT …

IF EXISTS(SELECT 1 FROM ...)
    SELECT …

我已经看到亚当·马查尼奇在对安德鲁·凯利的帖子的评论中指出了这一点 存在与计数(*) - 战斗永无止境:

有趣的是,在SQL Server 2005中,如果有一个索引 可用于允许搜索,COUNT(*)> 0 测试将得到优化,并且 行为与存在相同。

亚当在那里提供了一个演示。


2)有时EXISTSCOUNT更糟糕:

如果存在,则花费的时间比嵌入的选择语句长

检查存在与存在优于计数!不?

正如保罗·怀特(Paul White)所写:

使用 EXISTS 引入了行目标,其中优化程序生成 旨在快速找到第一行的执行计划。在这样做的过程中, 它假定数据是均匀分布的。例如,如果 统计数据显示,100,000 行中有 100 个预期匹配项,它 将假设它只需要读取 1,000 行才能找到第一个 火柴。

如果这样做,这将导致执行时间比预期的更长 事实证明,假设是错误的。例如,如果 SQL Server 选择 一种访问方法(例如无序扫描),恰好找到 第一个匹配值在搜索的后期很晚,它可能会导致 几乎完全扫描。另一方面,如果匹配的行碰巧 在前几行中找到,性能会非常好。 这是行目标的基本风险 - 不一致 性能。


如果你的数据分布是偏斜的,或者你预计在大多数情况下COUNT为零(即无论如何你都必须扫描整个表才能得到答案),那么你应该尝试在没有行目标的情况下获得计划(即没有EXISTS)。

您已经发现的一种明显方法是将COUNT的结果保存到变量中。

最新更新