Gra*_*ate 30 sql-server view computed-column
以下面的例子为例:
SELECT <CalculationA> As ColA,
<CalculationB> As ColB,
<CalculationA> + <CalculationB> As ColC
FROM TableA
Run Code Online (Sandbox Code Playgroud)
CalculationA 和CalculationB 会分别计算两次吗?
或者优化器是否足够聪明来计算一次并使用结果两次?
我想进行测试以查看自己的结果,但是,我不确定如何检查这样的事情。
我的假设是它会执行两次计算。
在哪种情况下,根据所涉及的计算,使用派生表或嵌套视图会更好吗?考虑以下:
SELECT TableB.ColA,
TableB.ColB,
TableB.ColA + TableB.ColB AS ColC,
FROM(
SELECT <CalculationA> As ColA,
<CalculationB> As ColB
FROM TableA
) As TableB
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我希望计算只执行一次?
请问有人可以证实或反驳我的假设吗?或者指导我如何为自己测试这样的东西?
谢谢。
Ran*_*gen 41
您需要的大部分信息都将在执行计划(和计划 XML)中。
拿这个查询:
SELECT COUNT(val) As ColA,
COUNT(val2) As ColB,
COUNT(val) + COUNT(val2) As ColC
FROM dbo.TableA;
Run Code Online (Sandbox Code Playgroud)
执行计划 (用sentryone plan explorer打开)显示了它经历了哪些步骤:
使用流聚合聚合EXPR1005和EXPR1006的值
如果我们想知道这些是什么,我们可以从查询计划 XML 中获取有关这些表达式的确切信息:
<ColumnReference Column="Expr1005" />
<ScalarOperator ScalarString="COUNT([Database].[dbo].[TableA].[val])">
<Aggregate AggType="COUNT_BIG" Distinct="false">
Run Code Online (Sandbox Code Playgroud)
使用第一个计算标量计算ColA & ColB:
最后一个计算标量是一个简单的加法:
这是在数据流动时读取它,理论上如果检查逻辑执行,您应该从左到右读取它。
在这种情况下,EXPR1004 正在调用其他表达式EXPR1002& EXPR1003。反过来,这些调用EXPR1005& EXPR1006。
CalculationA 和CalculationB 会分别计算两次吗?或者优化器是否足够聪明来计算一次并使用结果两次?
先前的测试表明,在这种情况下ColC,简化为定义为ColA&的计算的添加ColB。
因此,ColA&ColB只计算一次。
按 200 个不同的值分组
如果我们按 200 个不同的值 (val3) 分组,则显示相同:
SET STATISTICS IO, TIME ON;
SELECT SUM(val) As ColA,
SUM(val2) As ColB,
SUM(val) + SUM(val2) As ColC
FROM dbo.TableA
GROUP BY val3;
Run Code Online (Sandbox Code Playgroud)
汇总到这 200 个不同的值 val3
对 val 和 val2 进行求和,然后将它们加在一起用于 ColC:
即使我们对除一个非唯一值之外的所有值进行分组,计算标量也应该看到相同的加法。
向 ColA & ColB 添加函数
即使我们将查询更改为:
SET STATISTICS IO, TIME ON;
SELECT ABS(SUM(val)) As ColA,
ABS(SUM(val2)) As ColB,
SUM(val) + SUM(val2) As ColC
FROM dbo.TableA
Run Code Online (Sandbox Code Playgroud)
聚合仍然不会被计算两次,我们只是将ABS()函数添加到聚合的结果集中,即一行:
当然,运行SUM(ABS(ColA)&SUM(ABS(ColB))会使优化器无法使用相同的表达式来计算ColC。
如果您想在发生这种情况时深入了解,我会建议您阅读Paul White 的Query Optimizer Deep Dive - 第 1 部分(直到第 4 部分)。
深入了解查询执行阶段的另一种方法是添加以下提示:
OPTION
(
RECOMPILE,
QUERYTRACEON 3604,
QUERYTRACEON 8605
);
Run Code Online (Sandbox Code Playgroud)
这将公开优化器创建的输入树。
然后将要获得的两个先前计算值的相加ColC转换为:
AncOp_PrjEl COL: Expr1004
ScaOp_Arithmetic x_aopAdd
ScaOp_Identifier COL: Expr1002
ScaOp_Identifier COL: Expr1003
Run Code Online (Sandbox Code Playgroud)
该信息已经存在于Input Tree 中,甚至在简化阶段发生之前,这表明优化器立即知道它不必执行相同的计算两次。
如果计算的第一部分是实际计算 ( Col1 + Col2) 而不是函数,则为每个“计算”步骤执行单独的计算。
Run Code Online (Sandbox Code Playgroud)SELECT <CalculationA> As ColA, <CalculationB> As ColB, <CalculationA> + <CalculationB> As ColC FROM TableA
如果我们<CalculationA>从您的语句中替换为使用ColA和ColB来自表的有效计算,并在每个后续<CalculationB>,...步骤中重复此操作,那么将针对每个步骤单独执行计算结果的实际任务。
要重现我的语句,请将以下代码片段粘贴到 SQL Server Management Studio 中并运行。确保您已打开选项Include Actual Execution Plan。
它创建一个数据库,一个表,填充表并执行生成执行计划的计算。
创建数据库 Q252661
走
使用 Q252661
走
创建表 dbo.Q252661_TableA (
可乐 INT,
ColB INT,
ColC INT,
冷 INT)
走
插入 Q252661_TableA
(
可乐,
科尔B,
柯尔克,
寒冷的
)
价值观
(
1、
2、
3、
4
),(
2、
4、
8、
16
)
走
选择 ColA + ColB 作为 ColA,
ColC + Cold 作为 ColB,
ColA + ColB + ColC + Cold AS ColC
来自 Q252661_TableA
走
查询将运行并生成类似于以下内容的图形执行计划:
正如 Randi 的回答一样,我们将重点关注Compute Scalar运算符。
如果单击 SSMS 中的查询执行计划并右键单击以显示实际计划:
.. 您会发现以下 XML(重点介绍计算标量部分):
<ComputeScalar>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1003" />
<ScalarOperator ScalarString="[Q252661].[dbo].[Q252661_TableA].[ColA]+[Q252661].[dbo].[Q252661_TableA].[ColB]">
<Arithmetic Operation="ADD">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColA" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColB" />
</Identifier>
</ScalarOperator>
</Arithmetic>
</ScalarOperator>
</DefinedValue>
<DefinedValue>
<ColumnReference Column="Expr1004" />
<ScalarOperator ScalarString="[Q252661].[dbo].[Q252661_TableA].[ColC]+[Q252661].[dbo].[Q252661_TableA].[ColD]">
<Arithmetic Operation="ADD">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColC" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColD" />
</Identifier>
</ScalarOperator>
</Arithmetic>
</ScalarOperator>
</DefinedValue>
<DefinedValue>
<ColumnReference Column="Expr1005" />
<ScalarOperator ScalarString="[Q252661].[dbo].[Q252661_TableA].[ColA]+[Q252661].[dbo].[Q252661_TableA].[ColB]+[Q252661].[dbo].[Q252661_TableA].[ColC]+[Q252661].[dbo].[Q252661_TableA].[ColD]">
<Arithmetic Operation="ADD">
<ScalarOperator>
<Arithmetic Operation="ADD">
<ScalarOperator>
<Arithmetic Operation="ADD">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColA" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColB" />
</Identifier>
</ScalarOperator>
</Arithmetic>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColC" />
</Identifier>
</ScalarOperator>
</Arithmetic>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColD" />
</Identifier>
</ScalarOperator>
</Arithmetic>
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="23" EstimateCPU="8.07E-05" EstimateIO="0.0032035" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="2" LogicalOp="Table Scan" NodeId="1" Parallel="false" PhysicalOp="Table Scan" EstimatedTotalSubtreeCost="0.0032842" TableCardinality="2">
<OutputList>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColA" />
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColB" />
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColC" />
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColD" />
</OutputList>
<TableScan Ordered="false" ForcedIndex="false" ForceScan="false" NoExpandHint="false" Storage="RowStore">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColA" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColB" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColC" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" Column="ColD" />
</DefinedValue>
</DefinedValues>
<Object Database="[Q252661]" Schema="[dbo]" Table="[Q252661_TableA]" IndexKind="Heap" Storage="RowStore" />
</TableScan>
</RelOp>
</ComputeScalar>
Run Code Online (Sandbox Code Playgroud)
因此,在从实际表中检索值的情况下,每个单独的计算都会一次又一次地执行。以下 XML 片段来自上述摘要:
<ScalarOperator ScalarString="[Q252661].[dbo].[Q252661_TableA].[ColA]+[Q252661].[dbo].[Q252661_TableA].[ColB]">
<Arithmetic Operation="ADD">
Run Code Online (Sandbox Code Playgroud)
<Arithmetic Operation="ADD">执行计划有五个步骤。
CalculationA 和CalculationB 会分别计算两次吗?
是的,如果计算是根据示例的实际列总和。最后的计算将是 的总和CalculationA + CalculationB。
或者优化器是否足够聪明来计算一次并使用结果两次?
这取决于你在计算什么。- 在这个例子中:是的。- 在兰迪的回答中:不。
我的假设是它会执行两次计算。
您适合某些计算。
在哪种情况下,根据所涉及的计算,使用派生表或嵌套视图会更好吗?
正确的。
完成后,您可以再次删除数据库:
USE [master]
GO
DROP DATABASE Q252661
Run Code Online (Sandbox Code Playgroud)