此代码中的三个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;
生成这个计划:
为什么选择持久值的最终SELECT生成一个Compute Scalar运算符?
解决方法
只是为了总结评论中的实验结果,这似乎是一个边缘情况,当你在同一个表中有两个计算列时,一个持久化,一个不持久,并且它们都具有相同的定义.
在查询计划中
SELECT id5p FROM dbo.persist_test;
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);
给出输出
项目规范化之前的树
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,Value=5) AncOp_PrjEl COL: Expr1004 ScaOp_Arithmetic x_aopMult ScaOp_Identifier QCOL: [tempdb].[dbo].[persist_test].id ScaOp_Const TI(int,Value=5)
项目规范化后的树
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
因此,似乎所有计算列定义都被扩展,然后在Project Normalization阶段期间,所有相同的表达式都匹配回计算列,在这种情况下它恰好匹配id5.即它没有给持久列提供任何偏好.
如果使用以下定义重新创建表
CREATE TABLE dbo.persist_test ( id INT NOT NULL,id5p AS (5 * id) PERSISTED,id5 AS (5 * id) );
然后,通过读取持久化版本的数据而不是在运行时进行计算,将满足对id5或id5p的请求,因此匹配似乎按列顺序发生(至少在这种情况下).