SQL Server 查询 - ORDER BY 杀死小结果集的查询性能

Posted

技术标签:

【中文标题】SQL Server 查询 - ORDER BY 杀死小结果集的查询性能【英文标题】:SQL Server Query - ORDER BY killing query performance on small result set 【发布时间】:2013-06-13 16:30:54 【问题描述】:

我在 SQL Server 2008 R2 中有一个查询,格式如下:

SELECT TOP (2147483647) *
FROM (
    SELECT *
    FROM sub_query_a
) hierarchy
LEFT JOIN (
    SELECT *
    FROM sub_query_b
) expenditure
ON hierarchy.x = expenditure.x AND hierarchy.y = expenditure.y
ORDER BY hierarchy.c, hierarchy.d, hierarchy.e

hierarchy 子查询包含 UNIONS 和 INNER JOINS。 expenditure 子查询基于多个级别的子查询,并包含 UNIONS、INNER 和 LEFT JOINS,并最终包含 PIVOT 聚合。

hierarchy 子查询本身在 2 秒内运行并返回 467 行。 expenditure 子查询本身在 7 秒内运行并返回 458 行。同时,没有 ORDER BY 子句,查询在 11 秒内运行。但是,使用ORDER BY 子句,查询将在 11 分钟内运行。

实际执行计划揭示了不同之处。如果没有ORDER BY 子句,hierarchyexpenditure 子查询将分别运行一次,结果将Merge Join (Right Outer Join) 连接在一起。当包含ORDER BY 子句时,hierarchy 查询仍然运行一次,但expenditure 部分从层次结构查询中的每行运行一次,结果为Nested Loops (Left Outer Join) 连接一起。好像ORDER BY 子句导致expenditure 子查询成为相关子查询(事实并非如此)。

为了验证 SQL Server 确实能够在 11 秒内执行查询并生成排序结果集,作为测试,我创建了一个临时表并插入了查询的结果没有 ORDER BY 子句。然后我做了一个SELECT * FROM #temp_table ORDER BY c, d, e。整个脚本花费了预期的 11 秒,并返回了预期的结果。

我希望将ORDER BY 子句作为一个查询有效地进行查询——我不想仅仅为了启用#temp_table hacky 解决方案而创建存储过程。

关于此问题的原因或解决方法的任何想法?

【问题讨论】:

有多少估计的和实际的行进入嵌套循环?统计数据是最新的吗? 对一堆“联合和内连接”的输出进行排序无法与将输出作为一个整体排序到#temp 表中进行比较。基础表上是否有支持排序依据的索引?他们是工会还是工会所有人? TOP (2147483647) 到底在为你做什么?使您能够做一些糟糕的事情,例如将此查询放入带有 order by 子句的视图定义中? @MartinSmith:好主意。 expenditures 子查询中的子查询中的 1 个相关索引没有更新的统计信息。更新后,遗憾的是没有任何改善。从更新的实际执行计划来看:在非ORDER BY 版本中,hierarchy 子查询估计有 343 行(实际 467 行),expenditure 子查询估计有 106,245,000 行(实际 459 行)。在ORDER BY 版本中,hierarchy 子查询估计有 343 行(实际 467 行),expenditure 子查询估计有 104,648 行(实际 459 行)。 @AaronBertrand:涉及#temp_table 的测试是为了验证 SQL Server 实际上能够在合理的时间内产生我想要的结果。我知道比较两者的机制是没有用的。 hierarchy 子查询有 3 个 UNION ALLexpenditure 子查询有很多很多 UNION ALL 和一些 UNION 在必要时。基础表包含支持ORDER BY 子句的索引。您对 TOP (2147483647) 的看法是正确的;我这样做是有原因的,不会在此查询之上构建或运行任何视图或查询。有什么改进的想法吗? 【参考方案1】:

为了避免嵌套循环连接,你可以给编译器一个option

SELECT TOP (2147483647) *
FROM (
    SELECT *
    FROM sub_query_a
) hierarchy
LEFT JOIN (
    SELECT *
    FROM sub_query_b
) expenditure
ON hierarchy.x = expenditure.x AND hierarchy.y = expenditure.y
ORDER BY hierarchy.c, hierarchy.d, hierarchy.e
option (merge join, hash join)

我通常更喜欢让优化器找出正确的查询计划。但是,在极少数情况下,我会遇到与您类似的问题,需要提出建议以将其推向正确的方向

【讨论】:

我试过了,但查询没有运行。相反,我收到以下错误:由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示和不使用SET FORCEPLAN 的情况下重新提交查询。 @pcronin 。 . .您必须有一些非常有趣的子查询。我只能说,当我需要它时,它对我有用。我会留下答案,以防其他人有同样的想法。 我避免在网站上编写子查询的内容,因为它会占用几千行。特别是 sub_query_b 是基于多层视图的视图。目前我能想到几个需要改进的地方,但我所看到的没有什么能带来巨大的改进。绝对留下你的答案,因为它可能会在未来帮助人们,甚至是我。【参考方案2】:

感谢@MartinSmith 的评论,尽管我最终想要ORDER它。我想,如果我可以稍微优化一下那个版本,也许这也会使ORDER BY 版本受益。

正如我在 OP 中提到的,expenditure 子查询包含一个跨越另一个子查询的 PIVOT 聚合(我们称之为 unaggregated_expenditure)。我在PIVOTunaggregated_expenditure 子查询之间添加了一个层,它在PIVOT 之前在所需的几个数据透视列中聚合了所需的列。这增加了一点概念上的复杂性,但能够将来自PIVOT 的估计行数从 106,245,000 减少到 10,307。当将此更改应用于整个查询的 ORDER BY 版本时,会产生不同的实际执行计划,该计划能够在所需的 11 秒内处理和交付查询。

【讨论】:

以上是关于SQL Server 查询 - ORDER BY 杀死小结果集的查询性能的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 查询错误 -ORDER BY 子句在视图中无效

sql server 查询 order by 与 union 并替换多个列的空值

在SQL Server的子查询视图内联函数等数据库对象中,不应该单独使用ORDER BY语句

JPA 和 SQL Server 的 ORDER BY 子句中的列无效

使用带有 ORDER BY 的 SQL Server 查找累积总和

我可以在不使用 ROW_NUM () OVER (ORDER BY xxxxx) 的情况下在 Sql Server 中对查询进行分页吗?