【发布时间】:2010-08-26 17:19:21
【问题描述】:
我有一个大型查询,其中一个简单的子查询优化将其从 8 分钟缩短到 20 秒。我不确定我是否理解为什么优化会产生如此巨大的影响。
本质上,这是问题所在:
SELECT (bunch of stuff)
FROM
a LEFT OUTER JOIN b ON a.ID = b.a
LEFT OUTER JOIN c ON b.ID = c.b
...
...
INNER JOIN veryLargeTable
ON a.ID = veryLargeTable.a
AND veryLargeTable.PetID =
(SELECT id from Pets WHERE Pets.Name = 'Something') /* BAD! */
...
...
总共有 16 个连接表。如果我将 veryLargeTable 连接的第二个谓词替换为包含 petID 的预填充变量(而不是使用子查询),则整个查询会显着加快速度:
AND veryLargeTable.PetID = @petID /* Awesome! */
显然,(SELECT id from Pets WHERE Name = 'Something') 正在为每一行执行。有两点我不太明白:
据我所知,这是一个不相关的子查询。 Pets 表根本不是外部查询的一部分。不相关的子查询不是独立评估(并因此优化)吗?为什么这里不是这种情况?
执行计划有很大不同。在失败的情况下(上图),整个子树处理估计有 950k 行。在获胜的情况下(使用变量而不是子查询),估计只有大约 125k 行。这是怎么回事?如果存在该子查询,为什么还要涉及这么多行? Pets.Name 列肯定有唯一数据(但据我所知没有唯一约束)。
请注意,将谓词移动到 WHERE 子句在任何一种情况下都不会影响查询,正如我所期望的那样,因为它是一个 INNER JOIN。
感谢您的见解!
【问题讨论】:
-
使用变量会导致不同的计划。尽管变量的值在编译时是未知的,但它通常会导致更糟糕的计划。也许你只是在这个场合走运了。也许关注实际计划中的估计行数与实际行数,看看是否存在任何可能的统计问题。当您查看慢速运行的实际执行计划时,您真的可以看到子查询被执行了多次吗?
-
@Martin Smith - 我可以看到查询正在作为索引搜索执行,并且它被放入嵌套循环中,其中 RID 查找作为另一个输入。这是非常低的成本 - 但令人惊讶的是,进一步的一些操作会将其推入哈希匹配中,并在 veryLargeTable 上进行聚集索引扫描,这是一个巨大的成本。在查询的好版本中 - 这些操作都不存在。
标签: sql sql-server sql-server-2005 tsql subquery