Sql-Server

為什麼這個流聚合是必要的?

  • December 26, 2018

看看這個查詢。這很簡單(請參閱文章末尾的表和索引定義以及重現腳本):

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1 AND 1 = (SELECT 1);

注意:“AND 1 = (SELECT 1) 只是為了防止這個查詢被自動參數化,我覺得這讓這個問題感到困惑——不管有沒有那個子句,它實際上都得到了相同的計劃

這是計劃(粘貼計劃連結)

使用流 agg 進行計劃

由於那裡有一個“前 1”,我很驚訝地看到流聚合運算符。對我來說似乎沒有必要,因為保證只有一排。

為了測試這個理論,我嘗試了這個邏輯上等價的查詢:

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
GROUP BY Id;

這是那個計劃(粘貼計劃連結):

沒有流聚合的計劃

果然,group by plan 在沒有流聚合操作符的情況下也能過得去。

請注意,兩個查詢都從索引末尾“向後”讀取,並執行“top 1”以獲得最大修訂。

我在這裡想念什麼? 流聚合實際上是在第一個查詢中工作,還是應該能夠被消除(這只是優化器的一個限制,它不是)?

順便說一句,我意識到這不是一個非常實際的問題(兩個查詢都報告了 0 毫秒的 CPU 和經過的時間),我只是對這裡展示的內部結構/行為感到好奇。


這是我在執行上述兩個查詢之前執行的設置程式碼:

DROP TABLE IF EXISTS dbo.TheOneders;
GO

CREATE TABLE dbo.TheOneders
(
   Id INT NOT NULL,
   Revision SMALLINT NOT NULL,
   Something NVARCHAR(23),

   CONSTRAINT PK_TheOneders PRIMARY KEY NONCLUSTERED (Id, Revision)
);
GO

INSERT INTO dbo.TheOneders
   (Id, Revision, Something)
SELECT DISTINCT TOP 1000 
   1, m.message_id, 'Do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);

INSERT INTO dbo.TheOneders
   (Id, Revision, Something)
SELECT DISTINCT TOP 100 
   2, m.message_id, 'Do that thing you do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
GO

WHERE如果沒有行與子句匹配,您可以看到此聚合的作用。

SELECT MAX(Revision)
FROM   dbo.TheOneders
WHERE  Id = 1
      AND 1 = 1 /*To avoid auto parameterisation*/
      AND Id%3 = 4  /*always false*/

在這種情況下,零行進入聚合,但它仍然發出一個,因為NULL在這種情況下要返回正確的語義。

在此處輸入圖像描述

這是一個標量聚合,而不是向量聚合。

您的“邏輯等效”查詢不等效。添加GROUP BY Id將使其成為矢量聚合,然後正確的行為是不返回任何行。

有關此內容的更多資訊,請參閱Fun with Scalar and Vector Aggregates

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