sql-server – 你能解释一下这个执行计划吗?
副标题[/!--empirenews.page--]
当我遇到这件事时,我正在研究别的东西.我正在生成包含一些数据的测试表,并运行不同的查询以了解编写查询的不同方式如何影响执行计划.这是我用来生成随机测试数据的脚本: 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只是测试标志中的某些位是否打开. 通过在查询中尝试其他比较运算符,我得到了这些结果 +----------+----------+----------+-------------+----+----+---+---+---+---+ | 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运算符中排出的 根据我先前的假设,这种净效应是按以下顺序将范围呈现给合并间隔 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案例的解释. (编辑:广西网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |