Sql-Server

SQL Server 是否只在 SELECT 列表中執行一次計算?

  • January 6, 2020

舉個例子:

SELECT <CalculationA> As ColA,
      <CalculationB> As ColB,
      <CalculationA> + <CalculationB> As ColC
FROM TableA

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

在這種情況下,我希望計算只執行一次?

請問有人可以證實或反駁我的假設嗎?或者指導我如何為自己測試這樣的東西?

謝謝。

您需要的大部分資訊都將包含在執行計劃(和計劃 XML)中。

接受這個查詢:

SELECT COUNT(val) As ColA,
      COUNT(val2) As ColB,
      COUNT(val) +  COUNT(val2) As ColC
FROM dbo.TableA;

執行計劃 *(用sentryone plan explorer打開)*顯示了它經歷了哪些步驟:

在此處輸入圖像描述

使用流聚合聚合EXPR1005EXPR1006的值

在此處輸入圖像描述

如果我們想知道這些是什麼,我們可以從查詢計劃 XML 中獲取有關這些表達式的確切資訊:

<ColumnReference Column="Expr1005" />
<ScalarOperator ScalarString="COUNT([Database].[dbo].[TableA].[val])">
<Aggregate AggType="COUNT_BIG" Distinct="false">

使用第一個計算標量計算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;

匯總到這 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

聚合仍然不會計算兩次,我們只是將ABS()函式添加到聚合的結果集中,即一行:

在此處輸入圖像描述

在此處輸入圖像描述

當然,執行SUM(ABS(ColA)&SUM(ABS(ColB))會使優化器無法使用相同的表達式來計算ColC


如果您想在發生這種情況時更深入地了解,我會向您推薦Paul White 的Query Optimizer Deep Dive - 第 1 部分(直到第 4 部分)。

深入了解查詢執行階段的另一種方法是添加以下提示:

OPTION 
(
   RECOMPILE, 
   QUERYTRACEON 3604,
   QUERYTRACEON 8605
);

這將公開優化器創建的輸入樹。

然後將要獲取的兩個先前計算值相加ColC轉換為:

AncOp_PrjEl COL: Expr1004 

ScaOp_Arithmetic x_aopAdd

   ScaOp_Identifier COL: Expr1002 

   ScaOp_Identifier COL: Expr1003 

甚至在簡化階段發生之前,輸入樹中就已經存在此資訊,這表明優化器立即知道它不必執行相同的計算兩次。

引用自:https://dba.stackexchange.com/questions/252661