MSSQLSERVER执行计划详解
转自:https://www.cnblogs.com/knowledgesea/p/5005163.html
1. 序言
本篇主要目的有二:
- 看懂t-sql的执行计划,明白执行计划中的一些常识。
- 能够分析执行计划,找到优化sql性能的思路或方案。
如果你对sql查询优化的理解或常识不是很深入,那么推荐几篇博文给你:SqlServer性能检测和优化工具使用详细,sql语句的优化分析,T-sql语句查询执行顺序。
2. 执行计划简介
2.1 什么是执行计划?
提交的sql语句,经过"数据库查询优化器"分析, 生成多个数据库可以识别的高效执行查询方式。然后优化器会在众多执行计划中找出一个资源使用最少,而不是最快的执行方案,给你展示出来,可以是xml格式,文本格式,也可以是图形化的执行方案。
2.2 预估执行计划,实际执行计划
选择语句,点击上面其中一个执行计划,预估执行计划可以立即显示,而实际执行计划则需要执行sql语句后出现。预估执行计划不等于实际执行计划,但是绝大多数情况下实际的执行计划跟预估执行计划都是一致的。统计信息变更或者执行计划重编译等情况下,会造成不同。
SET STATISTICS PROFILE
和 SET STATISTICS XML
彼此互为对等物。 前者生成文本输出;后者生成 XML 输出。
在 SQL Server 的未来版本中,新的查询执行计划信息将只通过 SET STATISTICS XML
语句显示,而不通过 SET STATISTICS PROFILE
语句显示。
https://docs.microsoft.com/zh-cn/sql/relational-databases/performance/display-an-actual-execution-plan?view=sql-server-2017
2.3 为什么要读懂执行计划
首先执行计划让你知道你复杂的sql到底是怎么执行的,有没有按照你想的方案执行,有没有按照最高效的方式执行,使用啦众多索引的哪一个,怎么排序,怎么合并数据的,有没有造成不必要资源浪费等等。官方数据显示,执行t-sql存在问题,80%都可以在执行计划中找到答案。
2.4 针对图形化执行计划分析
执行计划,可以以文本,xml,图形化展示出来。本骗主要以图形化执行计划主导进行分析,然而执行计划中包含78个可用的操作符,本篇也只能对常用的进行分析,常用的几乎就包含你日常所有的了。Msdn上有图片介绍:https://msdn.microsoft.com/zh-cn/library/ms175913(v=sql.90).aspx
2.5 怎么看执行计划
图形化执行计划是 从上到下 从右到左 看的。
2.6 清除缓存的执行计划
dbcc freeprocache
dbcc flushprocindb(db_id)
3. 看懂图形化执行计划
3.1 连线
- 越粗表示扫描影响的行数愈多。
Actual Number of Rows
扫描中实际影响的的行数。Estimated Number of Rows
预估扫描影响的行数。Estimated row size
操作符生成的行的估计大小(字节)。Estimated Data Size
预估影响的数据的大小。
3.2 Tooltips 当前步骤(操作符)的详细信息
Note:这个tips的信息告诉我们执行的对象是什么,采用的操作操作是什么,查找的数据是什么,使用的索引是什么,排序与否,预估cpu、I/O、影响行数,实际行数等信息。具体参数清单参见msdn:https://msdn.microsoft.com/zh-cn/library/ms178071(v=sql.90).aspx
3.2.1 操作符的详细信息和统计数据
在数据库查询执行计划中,点击不同的操作符图标会显示工具提示(Tooltips),这些工具提示提供了关于该操作符的详细信息和统计数据。以下是通常在工具提示中看到的各个项的含义:
- Operator Type(操作符类型): 这是操作符的类型,例如 "Table Scan"、"Index Seek"、"Hash Match"、"Filter" 等。它告诉你该操作符的作用和功能。
- Estimate CPU Cost(预估 CPU 成本): 这是数据库引擎估计执行该操作符所需的 CPU 计算成本。它表示执行该操作符需要的计算资源。
- Estimate I/O Cost(预估 I/O 成本): 这是数据库引擎估计执行该操作符所需的 I/O 成本。它表示执行该操作符需要从存储中读取或写入的数据量。
- Estimate Operator Cost(预估操作符成本): 这是数据库引擎估计执行整个操作符所需的成本,通常是 CPU 成本和 I/O 成本的组合。
- Estimated Number of Rows(预估行数): 这是数据库引擎估计该操作符将返回的行数。它表示操作符的输出行数。
- Output List(输出列列表): 这是该操作符输出的列的列表。它列出了操作符将生成的列。
- Predicate(谓词): 这是操作符的过滤条件或谓词,通常是 WHERE 子句中的条件。它告诉你操作符如何筛选数据。
- Seek Predicates(查找谓词): 对于索引查找操作符,这是用于查找数据的谓词条件,通常是索引键的条件。
- Output Columns(输出列): 这是操作符的输出列,列出了操作符生成的列以及它们的数据类型。
- Warnings(警告): 这可能包括与操作符执行相关的任何警告或注意事项。
这些信息有助于你了解查询计划中各个操作符的作用、成本估算以及数据流向。它们对于分析查询性能和优化查询非常有用。不同数据库管理系统的工具提示可能会略有不同,但通常包含类似的信息。
3.3 Table Scan(表扫描)
当表中没有聚集索引,又没有合适索引的情况下,会出现这个操作。这个操作是很耗性能的,他的出现也意味着优化器要遍历整张表去查找你所需要的数据。
3.3.1 Table Scan(表扫描)详解
在数据库查询执行计划中,"Table Scan"(表扫描)是一种操作符,用于从数据库表中检索所有数据行以满足查询条件。这通常是一种较慢的数据访问方法,因为它需要扫描整个表格,而不是利用索引或其他优化方法来快速获取数据。
以下是 "Table Scan" 操作的一些关键点和含义:
- 全表扫描: "Table Scan" 表示数据库引擎需要遍历表格的每一行来查找满足查询条件的数据。这通常发生在没有适用的索引或优化方法的情况下,或者查询条件不允许有效地使用索引。
- 性能影响: "Table Scan" 操作通常对查询性能产生较大的影响,特别是在大型表格上。因为它需要检索整个表格,所以查询的响应时间可能会很长。
- 适用情况: "Table Scan" 通常在以下情况下发生:
- 表格没有适用的索引。
- 查询条件不允许使用索引,例如在某些情况下使用了函数或操作符。
- 查询需要检索表格的大部分数据。
- 查询优化: 在性能优化方面,通常会尽量避免 "Table Scan" 操作,以减少查询的响应时间。这可以通过创建适当的索引、优化查询条件、限制返回的行数等方式来实现。
总的来说,"Table Scan" 表示数据库引擎正在执行一种全表扫描操作,以满足查询条件。尽管它在某些情况下是必要的,但通常应尽量避免使用它,以提高查询性能。查询优化的一部分是识别并解决 "Table Scan" 引起的性能问题。
3.4 Clustered Index Scan(聚集索引扫描)、Index Scan(非聚集索引扫描)
这个图标两个操作都可以使用,一个聚集索引扫描,一个是非聚集索引扫描。
聚集索引扫描: 聚集索引的数据体积实际就是表本身,也就是说表有多少行多少列,聚集索引就有多少行多少列,那么聚集索引扫描就跟表扫描差不多,也要进行全表扫描,遍历所有表数据,查找出你想要的数据。
非聚集索引扫描: 非聚集索引的体积是根据你的索引创建情况而定的,可以只包含你要查询的列。那么进行非聚集索引扫描,便是你非聚集中包含的列的所有行进行遍历,查找出你想要的数据。
3.4.1 "聚集索引扫描"(Clustered Index Scan)
它是数据库查询执行计划中的一种操作,用于从包含表格数据的聚集索引中检索数据。聚集索引扫描通常发生在以下情况下:
- 全表扫描: 如果查询中没有使用任何筛选条件(例如
WHERE
子句)或没有使用覆盖索引,而且表格上存在聚集索引,数据库引擎可能会选择执行聚集索引扫描,以扫描整个表格以获取所需的数据。 - 不可覆盖查询: 如果查询需要检索的列不包含在聚集索引中,并且查询中没有适用的非聚集索引,数据库引擎可能会选择执行聚集索引扫描。这种情况下,引擎需要访问聚集索引来获取行的位置,然后再访问原始数据表格以检索所需的列数据。
聚集索引是表格的主要索引,它定义了表格中数据的物理排序顺序,并确定了表格的物理存储结构。因此,聚集索引扫描实际上是按照聚集索引的物理顺序来检索数据的过程。这通常会导致数据按照聚集索引的键值顺序进行访问。
需要注意的是,聚集索引扫描通常在没有更适合的索引可用的情况下发生,或者在需要检索整个表格或大部分表格数据的情况下。它可以是一种高成本的操作,特别是在大型表格上执行聚集索引扫描可能会导致性能下降。因此,在查询性能优化时,通常会考虑创建合适的索引以避免聚集索引扫描,并且尽量减少不可覆盖查询的情况。
3.4.2 非聚集索引扫描(Nonclustered Index Scan)
它是数据库查询执行计划中的一种操作,用于从非聚集索引中检索数据。非聚集索引扫描通常发生在以下情况下:
- 索引筛选: 如果查询中包含一个或多个筛选条件,并且这些条件与非聚集索引的键列相关,数据库引擎可能会选择执行非聚集索引扫描。这种情况下,索引用于定位满足筛选条件的行,而不需要扫描整个表格。
- 索引覆盖: 如果查询中包含的列都包含在非聚集索引中,而且查询不需要访问表格的原始数据,数据库引擎可能会选择执行非聚集索引扫描。这种情况下,非聚集索引包含了查询所需的所有信息,无需额外的数据访问。
非聚集索引是一种辅助索引,它包含了表格中特定列的键值和对应的行指针。非聚集索引扫描实际上是按照非聚集索引的键值顺序来检索数据的过程。与聚集索引不同,非聚集索引扫描通常不涉及对整个表格的物理顺序进行访问。
非聚集索引扫描通常是一种较为高效的数据检索方式,特别是当查询中包含了索引覆盖列时,因为它可以避免访问表格的原始数据。数据库引擎会根据查询的条件和需要检索的列来选择适当的索引扫描方式,以提高查询性能。
需要注意的是,索引的设计和查询的编写方式可以影响是否会发生非聚集索引扫描。为了优化查询性能,可以考虑创建适当的非聚集索引,以满足查询需求,并尽量避免全表扫描或不必要的数据访问。
3.5 Key Lookup(键值查找)
首先需要说的是查找,查找与扫描在性能上完全不是一个级别的,扫描需要遍历整张表,而查找只需要通过键值直接提取数据,返回结果,性能要好。
当你查找的列没有完全被非聚集索引包含,就需要使用键值查找在聚集索引上查找非聚集索引不包含的列。
另:
在数据库查询执行计划中,"Key Lookup"(也称为 "Bookmark Lookup")是一种查询操作,它通常出现在执行计划中,用于检索没有包含在索引中的其他列的值。Key Lookup 操作发生在已经使用一个索引执行过滤操作后,需要获取原始表格中的其他列数据时。
Key Lookup 操作通常涉及以下步骤:
- 数据库引擎首先使用索引来过滤查询中的行,这可以极大地加速数据检索,因为索引通常更小且更快。
- 但索引通常只包含部分列的数据,而不是表中的所有列。如果查询需要获取未包含在索引中的其他列数据,数据库引擎将执行 Key Lookup 操作。
- 在 Key Lookup 操作中,引擎将使用索引中的键值(通常是主键或唯一索引值)来查找原始表中对应的行,以检索需要的其他列数据。
- 这个操作可能涉及访问磁盘上的数据页,以获取缺失的列数据。这通常比直接从索引中获取数据要昂贵,因为需要更多的 I/O 操作。
Key Lookup 操作可以降低查询性能,特别是当需要大量的 Key Lookup 操作时。为了优化查询性能,可以考虑以下几种方法:
- 创建覆盖索引:一个覆盖索引包含了查询所需的所有列数据,从而避免了 Key Lookup 操作。
- 使用包含列:在索引中使用 INCLUDE 子句,以包括查询所需的其他列数据,减少 Key Lookup 的需要。
- 重新设计查询:考虑是否可以重写查询以减少 Key Lookup 操作的数量。
- 考虑内存和磁盘性能:Key Lookup 涉及访问磁盘,因此磁盘性能和内存大小可能会影响性能。
总之,Key Lookup 是一种在查询执行计划中用于检索未包含在索引中的其他列数据的操作,它可能会对查询性能产生负面影响,因此需要进行优化。
3.6 RID Lookoup(RID查找)
跟键值查找类似,只不过RID查找,是需要查找的列没有完全被非聚集索引包含,而剩余的列所在的表又不存在聚集索引,不能键值查找,只能根据行表示Rid来查询数据。
3.6.1 RID Lookup 通常用于检索与查询结果中的某些行相关的其他列的数据
在数据库查询执行计划中,"RID Lookup" 是一种操作符,通常用于检索与查询结果中的某些行相关的其他列的数据。"RID" 代表 "Row Identifier",表示行标识符,用于唯一标识表格中的每一行。
以下是 "RID Lookup" 操作的一些关键点和含义:
- 辅助检索操作: "RID Lookup" 是一种辅助操作,通常发生在查询中的其他操作之后。它用于查找满足查询条件的行的唯一标识符,然后使用这些标识符来检索其他列的数据。
- 行标识符: 在执行 "RID Lookup" 操作时,数据库引擎首先通过其他操作(例如索引扫描或表扫描)找到满足查询条件的行,并获取这些行的唯一标识符(RID)。然后,使用 RID 来定位和检索其他列的数据。
- 性能影响: "RID Lookup" 操作通常对查询性能有一定的影响,因为它需要额外的 I/O 操作来查找和检索数据。性能影响通常取决于行数和其他因素。
- 索引和聚集表: "RID Lookup" 操作通常与非聚集索引或聚集表一起使用。当查询需要检索非索引列的数据时,而非索引列没有包含在索引中时,就可能会出现 "RID Lookup"。
- 查询优化: 为了优化查询性能,可以考虑创建覆盖索引,这样索引将包含查询中所需的所有列,从而避免 "RID Lookup"。此外,可以考虑使用 JOIN 操作将所需的列合并到查询的主要表格中,以避免额外的检索操作。
总的来说,"RID Lookup" 表示数据库引擎正在执行一个额外的数据检索操作,以获取与查询结果中的某些行相关的其他列的数据。这个操作通常用于处理复杂的查询需求,但可能会对性能产生影响,因此需要在查询优化中进行考虑。
3.7 Clustered Index Seek(聚集索引查找)、Index Seek(非聚集索引查找)
聚集索引查找和非聚集索引查找都是使用该图标。
聚集索引查找: 聚集索引包含整个表的数据,也就是在聚集索引的数据上根据键值取数据。
非聚集索引查找: 非聚集索引包含创建索引时所包含列的数据,在这些非聚集索引的数据上根据键值取数据。
3.7.1 Clustered Index Seek(聚集索引查找)
在数据库查询执行计划中,"Clustered Index Seek"(聚集索引查找)是一种操作符,用于在聚集索引中查找满足查询条件的数据行。聚集索引是数据库表格的主要索引,它定义了表格数据的物理存储顺序。
以下是 "Clustered Index Seek" 操作的一些关键点和含义:
- 基于主键: 通常情况下,聚集索引是基于表格的主键列创建的,因此 "Clustered Index Seek" 操作通常用于根据主键查找数据。主键是一个唯一标识表格中每行的列。
- 用于快速查找: "Clustered Index Seek" 操作允许数据库引擎以非常高效的方式查找数据行,因为它是基于主键的查找操作。通过聚集索引查找,数据库引擎可以直接访问所需的数据行,而不需要扫描整个表格。
- 筛选条件: "Clustered Index Seek" 操作通常与查询中的筛选条件(例如
WHERE
子句)一起使用,以查找满足条件的数据行。 - 性能优化: "Clustered Index Seek" 操作通常是一种高效的操作,因为它允许数据库引擎直接访问所需的数据行,而不需要全表扫描或索引扫描。
总的来说,"Clustered Index Seek" 表示数据库引擎通过聚集索引的主键查找方式,以高效地定位并检索数据行,通常用于支持查询的筛选条件。这是一种性能较好的操作,用于加速数据检索。
3.7.2 Nonclustered Index Seek(非聚集索引查找)
在数据库查询执行计划中,"Nonclustered Index Seek"(非聚集索引查找)是一种操作符,用于在非聚集索引中查找满足查询条件的数据行。与聚集索引不同,非聚集索引是表格的辅助索引,它包含了列值和对应的行指针。
以下是 "Nonclustered Index Seek" 操作的一些关键点和含义:
- 非聚集索引使用: "Nonclustered Index Seek" 操作表示数据库引擎正在使用一个非聚集索引来查找数据,而不是主聚集索引。非聚集索引通常是根据一个或多个列的值创建的。
- 高效查找: 非聚集索引通常用于高效查找满足查询条件的数据行。它可以通过索引的 B 树结构迅速定位到索引中的键值,然后使用索引中的行指针访问对应的数据行。
- 支持筛选条件: "Nonclustered Index Seek" 操作通常与查询中的筛选条件(例如
WHERE
子句)一起使用,以查找满足条件的数据行。 - 适用于覆盖查询: 如果非聚集索引包括了查询所需的所有列(包括选择列表中的列),则查询可以成为覆盖查询(Covering Query),这意味着不需要额外的数据访问,因为索引已经包含了所有所需的信息。
- 性能优化: "Nonclustered Index Seek" 操作通常是一种高效的操作,因为它允许数据库引擎通过索引迅速查找所需的数据行,而不需要全表扫描。
总的来说,"Nonclustered Index Seek" 表示数据库引擎通过非聚集索引来高效地查找满足查询条件的数据行。这是一种用于加速数据检索的常见操作,尤其适用于涉及筛选条件的查询。通过适当设计和使用索引,可以优化查询性能。
3.8 Hash Match
这个图标有两种地方用到,一种是表关联,一种是数据聚合运算时。
再分别说这两中运算的前面,我先说说Hashing(编码技术) 和 Hash Table(数据结构)。
Hashing: 在数据库中根据每一行的数据内容,转换成唯一符号格式,存放到临时哈希表中,当需要原始数据时,可以给还原回来。类似加密解密技术,但是他能更有效的支持数据查询。
Hash Table: 通过hashing处理,把数据以key/value的形式存储在表格中,在数据库中他被放在tempdb中。
接下来,来说说Hash Math的表关联跟行数据聚合是怎么操作运算的。
表关联:
如上图,关联两个数据集时,Hash Match会把其中较小的数据集,通过Hashing运算放入HashTable中,然后一行一行的遍历较大的数据集与HashTable进行相应的匹配拉取数据。
数据聚合:当查询中需要进行Count/Sum/Avg/Max/Min
时,数据可能会采用把数据先放在内存中的HashTable中然后进行运算。
3.8.1 Hash Match 通常用于连接两个数据源,例如表格或视图,以执行联接操作
在数据库查询执行计划中,"Hash Match" 是一种操作符,通常用于连接两个数据源,例如表格或视图,以执行联接操作。"Hash Match" 使用哈希算法来处理数据,并将符合联接条件的数据行合并到结果集中。
以下是 "Hash Match" 操作的一些关键点和含义:
- 连接操作: "Hash Match" 通常用于连接两个数据源,例如两个表格,以执行 JOIN 操作。这可以是内连接、外连接或交叉连接,具体取决于查询的类型和条件。
- 哈希算法: "Hash Match" 使用哈希算法来处理连接操作。它将连接条件中的列值计算为哈希值,然后将哈希值相等的数据行合并在一起。这允许它在数据源中快速查找匹配的数据。
- 性能影响: "Hash Match" 操作通常对查询性能有一定的影响,特别是在大型数据集上执行连接操作时。哈希计算和数据分组需要一定的计算和内存资源。
- 适用情况: "Hash Match" 通常在以下情况下使用:
- 当连接条件不能有效使用索引时。
- 当连接操作涉及大量数据时。
- 当连接操作需要处理复杂的条件或子查询时。
- 查询优化: 为了优化查询性能,可以考虑创建适当的索引来改善连接操作的效率。此外,可以考虑使用其他 JOIN 类型(如 Nested Loops Join 或 Merge Join),具体取决于查询的特性。
总的来说,"Hash Match" 表示数据库引擎正在执行连接操作,使用哈希算法将符合联接条件的数据行合并在一起。这个操作通常用于连接大型数据集或处理复杂的连接条件,但需要谨慎考虑性能因素。在查询优化中,选择适当的连接策略和创建索引可以帮助提高性能。
3.9 Nested Loops
这个操作符号,把两个不同列的数据集汇总到一张表中。提示信息中的Output List中有两个数据集,下面的数据集(inner set)会一一扫描与上面的数据集(out set),知道扫描完为止,这个操作才算是完成。
3.9.1 Nested Loops(嵌套循环)
在数据库查询执行计划中,"Nested Loops"(嵌套循环)是一种连接操作符,通常用于执行连接操作,将两个数据集(通常是表格)中的数据进行联接。Nested Loops 连接是最简单和最常见的连接算法之一。
以下是 "Nested Loops" 操作的一些关键点和含义:
- 连接操作: "Nested Loops" 主要用于执行连接操作,通常是在 SQL 查询中的
JOIN
操作中使用。它用于将两个数据集中的行组合起来,以生成联接后的结果集。 - 嵌套循环: "Nested Loops" 得名于其算法。它使用两个循环,外部循环(Outer Loop)和内部循环(Inner Loop)。外部循环逐个遍历第一个数据集的行,而内部循环则在每次外部循环迭代时扫描第二个数据集,以查找与外部循环当前行匹配的行。
- 联接条件: "Nested Loops" 使用联接条件来确定哪些行应该被联接。通常,联接条件是一个比较操作,例如等值连接(e.g.,
INNER JOIN ON
)或不等值连接(e.g.,LEFT JOIN ON
)。这些条件用于确定哪些行应该被包含在联接结果中。 - 性能特点: "Nested Loops" 连接操作符的性能特点在于,它适用于某个输入数据集较小的情况,或者当有适用的索引可以支持连接条件时。如果一个输入数据集很大,而另一个输入数据集很小,"Nested Loops" 可能会变得昂贵,因为它需要在内部循环中执行多次查找操作。
- 连接类型: "Nested Loops" 连接可以用于不同类型的连接,包括内连接(Inner Join)、左连接(Left Join)、右连接(Right Join)等,具体取决于联接条件的定义。
总的来说,"Nested Loops" 表示数据库引擎正在使用嵌套循环算法来执行连接操作,这是一种常见的连接方法。它的性能特点适用于一些特定情况,但在处理大型数据集时可能会导致性能下降。对于复杂的连接操作和大型数据集,可能需要考虑其他连接算法,如哈希连接(Hash Join)或合并连接(Merge Join)。
3.10 Merge Join
这种关联算法是对两个已经排过序的集合进行合并。如果两个聚合是无序的则将先给集合排序再进行一一合并,由于是排过序的集合,左右两个集合自上而下合并效率是相当快的。
3.10.1 Merge Join 是一种连接操作符
在数据库查询执行计划中,"Merge Join" 是一种连接操作符,通常用于将两个已排序的数据源(通常是两个表格或索引扫描)合并在一起,以执行连接操作。"Merge Join" 需要输入数据源是按照连接条件的列进行排序的。
以下是 "Merge Join" 操作的一些关键点和含义:
- 连接操作: "Merge Join" 通常用于连接两个已排序的数据源,例如两个表格或两个索引,以执行 JOIN 操作。这可以是内连接、外连接或交叉连接,具体取决于查询的类型和条件。
- 已排序数据: "Merge Join" 要求输入数据源是按照连接条件的列进行排序的,以便可以按顺序合并数据。如果数据源未排序,通常需要在连接之前进行排序操作。
- 性能影响: "Merge Join" 操作通常在已排序的数据上执行,因此性能较好,特别是在大型数据集上执行连接操作时。它通常需要较少的计算资源和内存。
- 适用情况: "Merge Join" 通常在以下情况下使用:
- 当连接条件可以有效使用索引,并且数据源已按连接列排序时。
- 当连接操作需要连接两个大型数据集时。
- 当连接条件较简单时,不需要复杂的条件计算。
- 查询优化: 为了使用 "Merge Join",通常需要创建适当的索引,并确保数据源已按连接列排序。这将有助于提高连接操作的效率。
总的来说,"Merge Join" 表示数据库引擎正在执行连接操作,合并两个已排序的数据源以满足连接条件。这个操作通常用于连接大型数据集或已排序的数据,以提高连接操作的性能。在查询优化中,选择适当的连接策略和排序数据源可以帮助提高性能。
3.11 Sort(排序)
对数据集合进行排序,需要注意的是,有些数据集合在索引扫描后是自带排序的。
3.11.1 Sort 用于对查询结果集中的数据进行排序
在数据库查询执行计划中,"Sort" 是一种操作符,用于对查询结果集中的数据进行排序。排序操作按照指定的列或表达式将数据按升序或降序排列,以生成有序的结果。
以下是 "Sort" 操作的一些关键点和含义:
- 排序条件: "Sort" 操作通常在查询计划中的其他操作之后执行,以确保查询结果集按照指定的排序条件进行排列。排序条件可以是一个或多个列,也可以是一个表达式,根据这些条件来确定排序的顺序。
- 排序顺序: 排序可以是升序(Ascending,通常表示为 ASC)或降序(Descending,通常表示为 DESC)。升序排序会按升序排列结果,而降序排序会按降序排列结果。
- 性能影响: "Sort" 操作通常是比较耗费计算和内存资源的操作,特别是在大型数据集上执行排序操作时。因此,如果可以通过索引或其他方式避免排序操作,通常会更有利于性能。
- 示例: 例如,如果查询需要按照客户姓名的字母顺序对客户数据进行排序,则会在查询计划中出现 "Sort" 操作,以确保最终结果按姓名字母顺序排序。
- 索引排序: 在某些情况下,数据库引擎可能会使用已有的索引来支持排序操作,而不需要额外的排序步骤。这通常发生在查询的列上存在适当的索引,并且排序顺序与索引的顺序一致的情况下。
总的来说,"Sort" 表示数据库引擎正在执行排序操作,以生成按指定顺序排列的查询结果。排序通常是根据查询需求的一部分来执行的,但需要注意它的性能影响,特别是在大型数据集上执行排序操作时。在性能优化方面,可以考虑使用适当的索引来避免不必要的排序。
3.12 Filter(筛选)
根据出现在having之后的操作运算符,进行筛选
3.12.1 Filter 用于筛选数据,只返回符合特定条件的行
在数据库查询执行计划中,"Filter" 是一种操作符,用于筛选数据,只返回符合特定条件的行。"Filter" 操作通常在其他操作之后,用于从查询结果集中选择满足查询条件的数据。
以下是 "Filter" 操作的一些关键点和含义:
- 数据筛选: "Filter" 操作用于对查询结果集进行筛选,仅返回满足特定条件的行。这个条件通常是一个逻辑表达式,例如 WHERE 子句中的条件。
- 性能影响: "Filter" 操作通常对查询性能产生影响,特别是在大型数据集上执行复杂的筛选操作时。性能影响取决于筛选条件的复杂性和数据集的大小。
- 应用场景: "Filter" 操作在以下情况下使用:
- 当查询需要根据特定条件过滤数据时,例如查找特定日期范围内的订单或特定地区的客户。
- 当查询需要根据计算字段的结果进行筛选,例如查找销售额大于某个阈值的产品。
- 查询优化: 为了优化查询性能,可以考虑创建适当的索引来支持筛选操作。此外,合理编写查询条件和尽量减少筛选操作涉及的数据行数量也有助于提高性能。
总的来说,"Filter" 表示数据库引擎正在执行数据筛选操作,以满足查询条件并返回符合条件的数据行。这个操作通常用于在查询结果集中选择特定的数据,但需要谨慎考虑性能因素,以确保查询效率。在查询优化中,编写高效的筛选条件和创建适当的索引是提高性能的关键。
3.13 Computer Scalar
在需要查询的列中需要自定义列,比如count(*) as cnt,select name + '' + age
等会出现此符号。
3.13.1 Compute Scalar 主要用于计算标量表达式
在数据库查询执行计划中,"Compute Scalar" 是一种操作符,用于在执行查询时计算标量表达式的值。标量表达式是一个仅返回单个值而不是表格的表达式。"Compute Scalar" 操作通常在查询计划中的其他操作之前计算表达式的值。
以下是 "Compute Scalar" 操作的一些关键点和含义:
- 标量表达式计算: "Compute Scalar" 主要用于计算标量表达式,这些表达式可能包括数学运算、字符串操作、聚合函数或其他计算。计算的结果是一个标量值,通常用于查询的计算、过滤、排序或其他操作。
- 计算过程: 在执行查询计划的某个阶段,当需要计算标量表达式的值时,数据库引擎会执行 "Compute Scalar" 操作。这个操作会对每一行数据进行计算,然后将计算结果添加到查询结果集中。
- 性能影响: "Compute Scalar" 操作通常对查询性能没有明显的负面影响,因为它通常只涉及简单的计算。然而,在某些情况下,如果标量表达式非常复杂或需要大量计算资源,可能会对性能产生一些影响。
- 示例: 例如,可以在查询中使用 "Compute Scalar" 来计算两个列的和,或者计算一个日期列的差值。这些计算的结果可以用于排序、筛选或计算其他聚合函数。
总的来说,"Compute Scalar" 表示数据库引擎正在执行标量表达式的计算,以便为查询结果集中的每一行生成额外的计算值。这个操作通常用于支持查询中的计算需求。
3.14 Table Spool
"Table Spool" 是 SQL 查询执行计划中的一种操作符,它通常用于创建和维护一个临时表格(也称为 "spool"),以便在查询的后续步骤中使用。Table Spool 操作主要用于优化查询性能,特别是在某些情况下可以减少查询的复杂性或避免多次访问相同的数据。
Table Spool 操作通常出现在以下情况下:
- 排序操作: 当查询需要按照某列或多列排序结果时,Table Spool 可能会在排序之前创建一个临时表格,将数据存储在其中,以便在排序过程中使用。这可以避免对原始表格进行多次排序。
- 重复数据删除: 当查询需要从结果集中删除重复的行时,Table Spool 可以帮助创建一个包含唯一值的临时表格,以过滤重复数据。
- 延迟计算: 有时,Table Spool 可以用于延迟计算,只在需要时计算部分数据,而不是提前计算所有数据。
Table Spool 操作的使用是为了优化查询性能,因为它可以减少查询的复杂性,避免多次访问相同的数据,或者在需要时执行计算。然而,过多或不正确使用 Table Spool 可能会导致性能下降,因此需要谨慎使用并进行性能测试。在查询执行计划中,你可以查看 Table Spool 操作的详细信息,包括输入、输出行数和成本等,以帮助进行性能分析和调优。
3.15 Sequence Project(顺序项目)
在数据库查询执行计划中,"Sequence Project"(顺序项目)是一种操作符,通常用于计算和生成序列中的值,然后将这些值应用于查询结果集的行。Sequence Project 通常在查询计划中的其他操作之后执行。
以下是 "Sequence Project" 操作的一些关键点和含义:
- 生成序列值: Sequence Project 用于生成一系列值,这些值可以是整数、日期、标识符或其他类型的标量值。这些值通常是在查询的执行过程中生成的,而不是从表格中检索的。
- 应用于结果集: 生成的序列值可以应用于查询结果集的行,通常用于添加新的计算列或更改现有列的值。这可以用于创建排序、分组、分析函数或其他需要序列值的情况。
- 性能影响: "Sequence Project" 操作可能会对查询性能产生一些影响,因为它涉及生成序列值和修改结果集中的数据。性能影响取决于序列的大小和复杂性。
- 示例: 一个常见的示例是在查询中使用 "ROW_NUMBER()" 窗口函数,它可以为结果集中的行分配唯一的序号。这种操作通常需要 "Sequence Project" 来生成序列值并将其应用于结果集。
总的来说,"Sequence Project" 表示数据库引擎正在执行生成序列值并将其应用于查询结果的操作。这通常用于在查询中进行计算、排序或分组,并且可能会在性能优化中需要特别注意,以确保不会产生不必要的性能开销。
3.16 Stream Aggregate(流聚合)
在数据库查询执行计划中,"Stream Aggregate"(流聚合)是一种操作符,用于在查询结果集中执行聚合操作,如求和、计数、平均值等。Stream Aggregate 通常在查询计划中的其他操作之后执行,用于对已经过滤和排序的数据进行聚合操作。
以下是 "Stream Aggregate" 操作的一些关键点和含义:
- 聚合操作: "Stream Aggregate" 用于对一组数据进行聚合操作,以生成汇总结果。常见的聚合操作包括求和、计数、平均值、最大值、最小值等。
- 无需分组: 与 "Hash Match" 或 "Sort" 等需要对数据进行分组的操作不同,"Stream Aggregate" 不需要对数据进行明确的分组,它可以在数据流中按顺序计算聚合值。
- 性能影响: "Stream Aggregate" 操作通常性能较好,因为它不需要额外的内存或复杂的分组操作。它可以在处理数据流的同时即时计算聚合值。
- 示例: 一个常见的示例是计算一个查询结果集中的行数或求和某一列的值。在这种情况下,"Stream Aggregate" 可以在扫描和筛选数据后计算聚合值。
总的来说,"Stream Aggregate" 表示数据库引擎正在执行聚合操作,以计算一组数据的汇总值。这个操作通常用于生成查询结果中的统计信息或对数据进行简单的聚合计算。在查询优化中,了解何时使用 "Stream Aggregate" 可以帮助优化查询性能。
3.17 Top 表示对查询结果集返回的行数进行限制
在数据库查询执行计划中,"Top" 表示对查询结果集返回的行数进行限制。这个操作通常用于限制结果集中返回的前几行,以满足特定的需求,例如获取前 N 个最高销售额的产品或前 N 个最新的订单等。
以下是关于 "Top" 的一些关键点和含义:
- 行数限制: "Top" 操作允许你指定要返回的行数,通常使用数字 N 表示。查询结果将仅包含满足其他查询条件的前 N 行。
- 性能影响: "Top" 操作通常对查询性能影响较小,因为它仅涉及限制结果集的行数,而不会更改数据的计算或排序。性能影响主要取决于其他查询操作的复杂性。
- 应用场景: "Top" 操作在许多应用场景中很有用,如分页查询、获取前几名的排名数据、查找最高/最低值等。它通常与
ORDER BY
子句一起使用,以指定如何选择前 N 行。 - 示例: 例如,你可以使用 "Top" 操作来获取一个产品表格中销售额最高的前 10 个产品,或者获取一个订单表格中最新的前 5 个订单。
总的来说,"Top" 表示数据库查询计划正在执行一个限制行数的操作,用于返回查询结果中的前 N 行。这个操作通常用于满足特定的查询需求,以获取有限数量的行,而不是全部数据。
4. 根据执行计划细节要做的优化操作
这里会有很多建议给出,我不一一举例了,给出几个示例,想做到优化行家,多的还需要大家去悟去理解。
- 如果
select *
通常情况下聚集索引会比非聚集索引更优。 - 如果出现
Nested Loops
,需要查下是否需要聚集索引,非聚集索引是否可以包含所有需要的列。 Hash Match
连接操作更适合于需要做Hashing算法集合很小的连接。Merge Join
时需要检查下原有的集合是否已经有排序,如果没有排序,使用索引能否解决。- 出现表扫描,聚集索引扫描,非聚集索引扫描时,考虑语句是否可以加
where
限制,select *
是否可以去除不必要的列。 - 出现
Rid
查找时,是否可以加索引优化解决。 - 在计划中看到不是你想要的索引时,看能否在语句中强制使用你想用的索引解决问题,强制使用索引的办法
Select CluName1, CluName2 from Table with(index = IndexName)
。 - 看到不是你想要的连接算法时,尝试强制使用你想要的算法解决问题。强制使用连接算法的语句:
select * from t1 left join t2 on t1.id = t2.id option(Hash/Loop/Merge Join)
- 看到不是你想要的聚合算法是,尝试强制使用你想要的聚合算法。强制使用聚合算法的语句示例:
select age, count(age) as cnt from t1 group by age option(order/hash group)
- 看到不是你想要的解析执行顺序是,或这解析顺序耗时过大时,尝试强制使用你定的执行顺序。
option(force order)
- 看到有多个线程来合并执行你的sql语句而影响到性能时,尝试强制是不并行操作。
option(maxdop 1)
- 在存储过程中,由于参数不同导致执行计划不同,也影响啦性能时尝试指定参数来优化。
option(optiomize for(@name='zlh'))
- 不操作多余的列,多余的行,不做务必要的聚合,排序。