sql-server – 你能解释一下这个执行计划吗?

前端之家收集整理的这篇文章主要介绍了sql-server – 你能解释一下这个执行计划吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
当我遇到这件事时,我正在研究别的东西.我正在生成包含一些数据的测试表,并运行不同的查询以了解编写查询的不同方式如何影响执行计划.这是我用来生成随机测试数据的脚本:
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('t') AND type in (N'U'))
DROP TABLE t
GO

CREATE TABLE t 
(
 c1 int IDENTITY(1,1) NOT NULL,c2 int NULL
) 
GO

insert into t
select top 1000000 a from
(select t1.number*2048 + t2.number a,newid() b
from [master]..spt_values t1 
cross join  [master]..spt_values t2
where t1.[type] = 'P' and t2.[type] = 'P') a
order by b
GO

update t set c2 = null
where c2 < 2048 * 2048 / 10
GO


CREATE CLUSTERED INDEX pk ON [t] (c1)
GO

CREATE NONCLUSTERED INDEX i ON t (c2)
GO

现在,根据这些数据,我调用了以下查询

select * 
from t 
where 
      c2 < 1048576 
   or c2 is null
;

令我惊讶的是,为此查询生成的执行计划是this.(对不起外部链接,它太大了,不适合这里).

有人可以向我解释所有这些“Constant Scans”和“Compute Scalars”的内容吗?发生了什么?

|--Nested Loops(Inner Join,OUTER REFERENCES:([Expr1010],[Expr1011],[Expr1012]))
       |--Merge Interval
       |    |--Sort(TOP 2,ORDER BY:([Expr1013] DESC,[Expr1014] ASC,[Expr1010] ASC,[Expr1015] DESC))
       |         |--Compute Scalar(DEFINE:([Expr1013]=((4)&[Expr1012]) = (4) AND NULL = [Expr1010],[Expr1014]=(4)&[Expr1012],[Expr1015]=(16)&[Expr1012]))
       |              |--Concatenation
       |                   |--Compute Scalar(DEFINE:([Expr1005]=NULL,[Expr1006]=NULL,[Expr1004]=(60)))
       |                   |    |--Constant Scan
       |                   |--Compute Scalar(DEFINE:([Expr1008]=NULL,[Expr1009]=(1048576),[Expr1007]=(10)))
       |                        |--Constant Scan
       |--Index Seek(OBJECT:([t].[i]),SEEK:([t].[c2] > [Expr1010] AND [t].[c2] < [Expr1011]) ORDERED FORWARD)

解决方法

常量扫描每个都会生成一个没有列的内存行.顶部计算标量输出一行有3列
Expr1005    Expr1006    Expr1004
----------- ----------- -----------
NULL        NULL        60

底部计算标量输出一行有3列

Expr1008    Expr1009    Expr1007
----------- ----------- -----------
NULL        1048576        10

连接运算符将这两行联合在一起并输出3列,但现在它们已重命名

Expr1010    Expr1011    Expr1012
----------- ----------- -----------
NULL        NULL        60
NULL        1048576     10

Expr1012列是一组标志used internally to define certain seek properties for the Storage Engine.

输出2行的下一个计算标量

Expr1010    Expr1011    Expr1012    Expr1013    Expr1014    Expr1015
----------- ----------- ----------- ----------- ----------- -----------
NULL        NULL        60          True        4           16            
NULL        1048576     10          False       0           0

最后三列定义如下,在呈现给合并间隔运算符之前仅用于排序目的

[Expr1013] = Scalar Operator(((4)&[Expr1012]) = (4) AND NULL = [Expr1010]),[Expr1014] = Scalar Operator((4)&[Expr1012]),[Expr1015] = Scalar Operator((16)&[Expr1012])

Expr1014和Expr1015只是测试标志中的某些位是否打开.
如果4的位都打开且Expr1010为NULL,则Expr1013似乎返回布尔列true.

通过在查询中尝试其他比较运算符,我得到了这些结果

+----------+----------+----------+-------------+----+----+---+---+---+---+
| Operator | Expr1010 | Expr1011 | Flags (Dec) |       Flags (Bin)       |
|          |          |          |             | 32 | 16 | 8 | 4 | 2 | 1 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
| >        | 1048576  | NULL     |           6 |  0 |  0 | 0 | 1 | 1 | 0 |
| >=       | 1048576  | NULL     |          22 |  0 |  1 | 0 | 1 | 1 | 0 |
| <=       | NULL     | 1048576  |          42 |  1 |  0 | 1 | 0 | 1 | 0 |
| <        | NULL     | 1048576  |          10 |  0 |  0 | 1 | 0 | 1 | 0 |
| =        | 1048576  | 1048576  |          62 |  1 |  1 | 1 | 1 | 1 | 0 |
| IS NULL  | NULL     | NULL     |          60 |  1 |  1 | 1 | 1 | 0 | 0 |
+----------+----------+----------+-------------+----+----+---+---+---+---+

从中我推断Bit 4的意思是“具有范围的开始”(而不是无界),而第16位意味着范围的起点是包容性的.

这个6列结果集是从SORT运算符中排出的
Expr1013 DESC,Expr1014 ASC,Expr1010 ASC,Expr1015 DESC.假设True表示为1而False表示为0,则先前表示的结果集已按此顺序表示.

根据我先前的假设,这种净效应是按以下顺序将范围呈现给合并间隔

ORDER BY 
          HasStartOfRangeAndItIsNullFirst,HasUnboundedStartOfRangeFirst,StartOfRange,StartOfRangeIsInclusiveFirst

合并间隔运算符输出2行

Expr1010    Expr1011    Expr1012
----------- ----------- -----------
NULL        NULL        60
NULL        1048576     10

对于发射的每一行,执行范围搜索

Seek Keys[1]: Start:[dbo].[t].c2 > Scalar Operator([Expr1010]),End: [dbo].[t].c2 < Scalar Operator([Expr1011])

所以看起来似乎有两次搜寻.一个显然> NULL AND< NULL和一个> NULL AND<然而,传入的标志似乎将其修改为IS NULL和< 1048576.分别为1048576.希望@sqlkiwi可以澄清这一点并纠正任何不准确之处!

如果您稍微更改查询

select *
from t 
where 
      c2 > 1048576 
   or c2 = 0
;

然后,使用具有多个搜索谓词的索引搜索,该计划看起来更简单.

该计划显示了Seek Keys

Start: c2 >= 0,End: c2 <= 0,Start: c2 > 1048576

sqlKiwi在对earlier linked blog post评论中给出了为什么这个更简单的计划不能用于OP案例的解释.

具有多个谓词的索引查找不能混合不同类型的比较谓词(即,在OP中的情况下为Is和Eq).这只是产品的当前限制(并且可能是为什么使用> =和< =而不是仅仅为查询获得的直接相等性来实现上一个查询c2 = 0中的相等性测试的原因c2 = 0或c2 = 1048576.

猜你在找的MsSQL相关文章