当我选择一个持久化的计算列时,为什么 SQL Server 会“计算标量”?

Nic*_*mas 21 sql-server-2008 sql-server execution-plan

SELECT这段代码中的三个语句

USE [tempdb];
GO

SET NOCOUNT ON;

CREATE TABLE dbo.persist_test (
      id            INT NOT NULL
    , id5           AS (id * 5)
    , id5p          AS (id * 5) PERSISTED
);

INSERT INTO dbo.persist_test (id)
VALUES (1), (2), (3);

SELECT id
FROM dbo.persist_test;

SELECT id5
FROM dbo.persist_test;

SELECT id5p
FROM dbo.persist_test;

DROP TABLE dbo.persist_test;
Run Code Online (Sandbox Code Playgroud)

生成这个计划:

执行计划

为什么SELECT选择持久值的 final生成计算标量运算符?

Mar*_*ith 15

只是为了总结评论中的实验结果,这似乎是一种边缘情况,当您在同一个表中有两个计算列时,一个persisted和一个没有持久化,并且它们都具有相同的定义。

在查询计划中

SELECT id5p
FROM dbo.persist_test;
Run Code Online (Sandbox Code Playgroud)

表扫描persist_test仅发出id列。下一个计算标量乘以 5 并输出一个名为的列,id5尽管该列在查询中甚至没有被引用。最终的计算标量采用 的值id5并将其输出为名为 的列id5p

使用Query Optimizer Deep Dive – Part 2 中解释的跟踪标志(免责声明:这些跟踪标志未记录/不受支持)并查看查询

SELECT id5,
       id5p,
       ( id * 5 )
FROM   dbo.persist_test 
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8606);
Run Code Online (Sandbox Code Playgroud)

给出输出

项目规范化前的树

LogOp_Project

    LogOp_Get TBL: dbo.persist_test dbo.persist_test TableID=1717581157 TableReferenceID=0 IsRow: COL: IsBaseRow1002 

    AncOp_PrjList 

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5p

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)

        AncOp_PrjEl COL: Expr1004 

            ScaOp_Arithmetic x_aopMult

                ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id

                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)
Run Code Online (Sandbox Code Playgroud)

项目规范化后的树

LogOp_Project

    LogOp_Get TBL: dbo.persist_test dbo.persist_test TableID=1717581157 TableReferenceID=0 IsRow: COL: IsBaseRow1002 

    AncOp_PrjList 

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5

        AncOp_PrjEl QCOL: [tempdb].[dbo].[persist_test].id5p

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5

        AncOp_PrjEl COL: Expr1004 

            ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id5
Run Code Online (Sandbox Code Playgroud)

因此,所有计算列定义似乎都被扩展了,然后在项目规范化阶段,所有相同的表达式都匹配回计算列,并且id5在这种情况下恰好匹配。即它不给persisted列任何偏好。

如果使用以下定义重新创建表

CREATE TABLE dbo.persist_test (
      id            INT NOT NULL
    , id5p          AS (5 * id) PERSISTED
    , id5           AS (5 * id)
);
Run Code Online (Sandbox Code Playgroud)

然后通过读取数据的持久版本而不是在运行时进行计算来满足对id5或的请求,id5p因此匹配似乎以列顺序发生(至少在这种情况下)。