SQL Server 运行计划操作符具体解释(3)——计算标量(Compute Scalar)

接上文:SQL Server 运行计划操作符详细解释(2)——串联(Concatenation ) 


前言:


前面两篇文章介绍了关于串联(Concatenation)断言(Assert)操作符,本文介绍第三个常见的操作符计算标量(Compute Scalar)。这个操作符的名字比較直观——进行一个标量计算并返回计算值。

官方说明:Compute Scalar 运算符通过对表达式求值来生成计算标量值。该值能够返回给用户、在查询中的其它位置引用或二者皆可。比如。在筛选谓词或联接谓词中就会出现二者皆可的情况。

该操作符的图标为:Compute Scalar 运算符图标,它既是一个逻辑操作符。也是一个物理操作符。这个操作符可能不easy引起用户注意,由于一般我们看运行计划是由于语句有问题,而有问题的语句又一般是比較复杂或混乱的,这些语句生成的运行计划往往也很复杂。相对于整个运行计划来说,这个操作符一般是比較小开销的。
可是这个操作符之所以重要或常见,是由于它一般是由于游标处理或其它一些大范围查找引起的。这些操作可能在CPU存在压力时变得雪上加霜。

演示:


使用TempDB做測试是一个不错的选择。简单重新启动一下SQL 服务就可以清空过去的操作。只是假设你发现重新启动后还在,那最好还是检查一下是否建到Model数据库或者设置为启动时运行。

以下代码在TempDB中创建一个表,插入10000行数据后,循环100次进行数据检查:


USE tempdb
GO
CREATE TABLE test(ID   Int Identity(1,1) PRIMARY KEY,
                Name VarChar(250)  DEFAULT NewID())
GO
SET NOCOUNT ON
GO
INSERT INTO test DEFAULT VALUES
GO 10000  --循环插入10000行数据

GO
--以下代码循环100次,推断是否存在某个ID
DECLARE @I Int
SET @I = 0
WHILE @I < 100
BEGIN
  IF EXISTS(SELECT ID FROM test WHERE ID = @I)
  BEGIN
    PRINT '存在这个ID'
  END
  SET @I = @I + 1;
END
GO

看一下图形化运行计划:




截图中红框部分表明使用了计算标量操作符,使用前面的方法。检查文本化运行计划:

SET SHOWPLAN_TEXT ON 
GO
DECLARE @I Int
SET @I = 0
WHILE @I < 100
BEGIN
  IF EXISTS(SELECT ID FROM test WHERE ID = @I)
  BEGIN
    PRINT '存在这个ID'
  END
  SET @I = @I + 1;
END



能够看到运行计划使用计算标量操作符来检查嵌套循环(Nested Loop)是否返回了值。也就是说用于实现IF EXISTS操作。

假设使用Profiler来抓取信息,记住一下CPU开销:



以下改写一下语句来避免这个操作符:

DECLARE @I Int, @Var Int
SET @I = 0
WHILE @I < 100
BEGIN
  SELECT @Var = ID FROM test WHERE ID = @I
  IF @@ROWCOUNT > 0
  BEGIN
    PRINT '存在这个ID'
  END
  SET @I = @I + 1;
END
GO

再看看图形化运行计划:



及Profiler信息:




假设再检查文本化运行计划就能够看到仅仅有一个操作符:

       |--Clustered Index Seek(OBJECT:([tempdb].[dbo].[test].[PK__test__3214EC27D8827737]), SEEK:([tempdb].[dbo].[test].[ID]=[@I]) ORDERED FORWARD)

对照Profiler中的数据,没有使用计算标量的运行计划消耗更少的CPU和运行时间去完毕结果。这里主要是演示计算标量,所以不正确写法做更深入的研究。

可是从写法上看,使用了@@rowcount函数替代IF EXISTS,有时候会有一定的帮助。当然,并非绝对的。


假设你认为是数据量的原因,最好还是再看看以下的脚本:

DECLARE @Tab TABLE(ID SmallInt PRIMARY KEY)
SELECT 'A' + ' - ' + 'B' FROM @Tab

然后看看图形化运行计划:



和文本化运行计划:

  |--Compute Scalar(DEFINE:([Expr1002]='A - B'))
       |--Clustered Index Scan(OBJECT:(@Tab))

这个语句仅仅是简单地进行字符串拼接,可是也使用了计算标量运算符,原因能够查看运行计划的解释:




总结:

正如一直以来的解释,每一个操作符的出现都有其原因和作用,并不能简单地下定论这个操作符是好还是坏。可是某些操作符确实意味着性能问题,所以假设精力同意,也应该对常见的操作符进行一定程度的研究。当发现某个低效查询中出现这个操作符时,最好还是想想其原因,并尝试能否进行优化。优化的原则则是依据其含义而定,既然这个操作符是依据现有值计算新值,那么我们的核心方案应该是降低这样的操作的数据量或者预先计算新值。总的而言。详细情况详细分析。
下一篇将介绍一个更为常见而且通常意味着有优化空间的操作符:键查找:Key Lookup   
posted @ 2017-06-16 16:37  lytwajue  阅读(369)  评论(0编辑  收藏  举报