我有一个很大的查询,其中一个简单的子查询优化从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行。在win情况下(使用变量而不是子查询),估计的行只有大约125k。这是怎么回事?为什么有更多的行涉及如果该子查询在那里? Pets.Name列肯定有唯一的数据(但据我所知,没有唯一的约束)。
请注意,将谓词移至WHERE子句不会影响查询,正如我所期望的那样,因为它是INNER JOIN。
深入了解!
使用变量可能导致不同的计划。它通常会导致更糟糕的计划,因为变量的值在编译时并不知道。也许你在这个场合很幸运。也许专注于实际计划中的估计行数与实际行数,以查看是否有任何可能的统计问题。当您查看缓慢运行的实际执行计划时,您是否可以看到多次执行的子查询? – 2010-08-26 17:29:33
@Martin Smith - 我可以看到正在执行的查询作为索引查找,并将其作为其他输入放入带有RID查找的嵌套循环中。这是非常低的成本 - 但令人惊讶的是,进一步的一些操作,它将它推到哈希匹配与非常大表中的集群索引扫描,这是一个巨大的成本。在查询的好版本中 - 这些操作都不存在。 – womp 2010-08-26 17:52:43