目录
什么是查询计划?
执行计划的一部分
查看查询计划
计划消除SQL的神秘性
为什么查询计划失败
本课程的所有示例均基于Microsoft SQL Server Management Studio和AdventureWorks2012数据库。让我们开始学习查询计划。
什么是查询计划?查询计划是数据库管理系统为完成查询而执行的一组步骤。我们有查询计划的原因是,你编写的SQL可能会声明你的意图,但它没有告诉SQL使用的确切逻辑流程。查询优化器确定了。结果是查询计划。
在SQL Server中,查询计划称为执行计划。
执行计划的一部分执行计划有几个部分值得一提。
首先,每个计划由一个或多个执行步骤组成。这些步骤描述了用于创建查询结果的数据库操作。了解步骤及其含义非常重要。例如,某些步骤(例如嵌套循环)可能非常昂贵。
在MS SQL Server中,您可以将鼠标悬停在步骤上以查看更多信息,例如步骤的相对成本,处理的行数以及SQL服务器将用于完成它的实际指令。此信息使您可以进一步了解步骤执行的工作量。
该计划的另一部分是从一个步骤到另一个步骤的流程。在简单查询中,这是顺序的。一步的输出流入另一步。但是,随着查询变得更加复杂,该计划包含多个分支。
每个分支代表一个不同的数据源,例如查询中的另一个表,并且这些分支最终使用诸如合并步骤之类的步骤进行组合。
您还可以将鼠标悬停在分支上以查看该步骤输出的行数。这是一个简单查询的完整查询计划。
阅读查询计划时,请从右向左阅读。首先执行右侧的步骤,并将结果输入左侧的下一步。
查看查询计划执行的每个查询都会生成查询计划。使用Microsoft SSMS(SQL Server Management Studio)很容易看到该计划。
为此,请创建一个查询,然后确保选中“包括实际执行计划(1)”。查询运行(2)后,将显示计划。
您已创建计划,运行查询,然后选择执行计划选项卡(3)进行查看。
计划消除SQL的神秘性在研究新文章的过程中,我经常遇到诸如“子查询效率低于连接”之类的语句。这让我思考。这些说法是正确的观点还是有确凿的事实?
为了理解子查询和连接的执行情况,我决定查看他们的查询计划,以了解优化器如何创建每个查询计划。我真的希望在子查询的计划中看到一些代价高昂的步骤。
令我惊讶的是,计划几乎相同。
这是我用作测试的子查询
SELECT SalesOrderID,
OrderDate,
TotalDue,
(SELECT COUNT(SalesOrderDetailID)
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = SO.SalesOrderID) as LineCount
FROM Sales.SalesOrderHeader SO
这是查询计划。
这里是等效的INNER JOIN
SELECT SO.SalesOrderID,
OrderDate,
TotalDue,
COUNT(SOD.SalesOrderDetailID) as LineCount
FROM Sales.SalesOrderHeader SO
INNER JOIN Sales.SalesOrderDetail SOD
ON SOD.SalesOrderID = SO.SalesOrderID
GROUP BY SO.SalesOrderID, OrderDate, TotalDue
它们几乎相同。惊讶吗?我只是想了想才明白。SQL是声明性的,即我们用它来表达我们的意图,而不是如何实际从数据库中提取数据。
在检查了SQL之后,DBMS分解了这些步骤,检查了完成查询并生成计划的最有效方法。在这两种情况下,计划都是一样的。
在有疑问的情况下从中学习的经验教训检查执行计划。如果您认为查询运行缓慢且等效查询(例如连接)可能更快,请编写一个并检查计划。检查哪些使用更有效的步骤。这比猜测要好得多。随着阅读计划越来越好,您将开始注意到有关数据库的事情,例如是否需要添加索引。
总的来说,我相信DBMS做出正确的决定并为我的查询生成最佳计划。在某些情况下不能再需要优化。
一个好的计划和它所依据的信息一样好。当DBMS解析您的SQL并生成查询计划时,它会经过许多选择并选择成本最低的那个。
成本基于数据库统计信息,例如表行计数。如果统计数据已过期,则计划基于不良信息。此外,缺乏指数也会影响计划。
我还想指出,虽然子查询和内部联接计划对于我在AdventureWorks2012数据库中显示的示例来说是相同的,但对于其他数据库或供应商(如Oracle)可能并非如此。应考虑每种情况,因为有许多因素会影响优化器。