Sql-Server
為什麼這個流聚合是必要的?
看看這個查詢。這很簡單(請參閱文章末尾的表和索引定義以及重現腳本):
SELECT MAX(Revision) FROM dbo.TheOneders WHERE Id = 1 AND 1 = (SELECT 1);
注意:“AND 1 = (SELECT 1) 只是為了防止這個查詢被自動參數化,我覺得這讓這個問題感到困惑——不管有沒有那個子句,它實際上都得到了相同的計劃
這是計劃(粘貼計劃連結):
由於那裡有一個“前 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。