強制流不同
我有一張這樣的桌子:
CREATE TABLE Updates ( UpdateId INT NOT NULL IDENTITY(1,1) PRIMARY KEY, ObjectId INT NOT NULL )
本質上是跟踪 ID 不斷增加的對象的更新。
此表的使用者將選擇 100 個不同的對象 ID 的塊,按
UpdateId
特定的UpdateId
. 本質上,跟踪它停止的位置,然後查詢任何更新。我發現這是一個有趣的優化問題,因為我只能通過編寫查詢來生成一個最大優化的查詢計劃,這些查詢由於索引而恰好執行我想要的操作,但不能保證我想要的:
SELECT DISTINCT TOP 100 ObjectId FROM Updates WHERE UpdateId > @fromUpdateId
@fromUpdateId
儲存過程參數在哪裡。有以下計劃:
SELECT <- TOP <- Hash match (flow distinct, 100 rows touched) <- Index seek
由於
UpdateId
正在使用的索引上的搜尋,結果已經很好,並且像我想要的那樣從最低到最高更新 ID 排序。這會生成一個流程不同的計劃,這就是我想要的。但是排序顯然不能保證行為,所以我不想使用它。這個技巧也會產生相同的查詢計劃(儘管有一個冗餘的 TOP):
WITH ids AS ( SELECT ObjectId FROM Updates WHERE UpdateId > @fromUpdateId ORDER BY UpdateId OFFSET 0 ROWS ) SELECT DISTINCT TOP 100 ObjectId FROM ids
不過,我不確定(也不懷疑)這是否真的能保證訂購。
我希望 SQL Server 足夠智能以簡化的一個查詢是這樣的,但它最終會生成一個非常糟糕的查詢計劃:
SELECT TOP 100 ObjectId FROM Updates WHERE UpdateId > @fromUpdateId GROUP BY ObjectId ORDER BY MIN(UpdateId)
有以下計劃:
SELECT <- Top N Sort <- Hash Match aggregate (50,000+ rows touched) <- Index Seek
我正在嘗試找到一種方法來生成具有索引搜尋的最佳計劃
UpdateId
和不同的流程以刪除重複ObjectId
的 s。有任何想法嗎?如果需要,請提供樣本數據。對像很少會有一個以上的更新,並且在一組 100 行中幾乎不應該有一個以上的更新,這就是為什麼我追求一個流 distinct,除非有更好的東西我不知道?但是,不能保證單個
ObjectId
表中的行數不會超過 100 行。該表有超過 1,000,000 行,預計會快速增長。假設 this 的使用者有另一種方法可以找到合適的 next
@fromUpdateId
。無需在此查詢中返回它。
SQL Server 優化器無法在您需要的保證下生成您所追求的執行計劃,因為Hash Match Flow Distinct運算符不保持順序。
不過,我不確定(也不懷疑)這是否真的能保證訂購。
在許多情況下,您可能會觀察到訂單保留,但這是一個實現細節;沒有保證,所以你不能依賴它。與往常一樣,呈現順序只能由頂級
ORDER BY
子句保證。例子
下面的腳本顯示 Hash Match Flow Distinct 不保留順序。它在兩列中設置了匹配數字 1-50,000 的表:
IF OBJECT_ID(N'dbo.Updates', N'U') IS NOT NULL DROP TABLE dbo.Updates; GO CREATE TABLE Updates ( UpdateId INT NOT NULL IDENTITY(1,1), ObjectId INT NOT NULL, CONSTRAINT PK_Updates_UpdateId PRIMARY KEY (UpdateId) ); GO INSERT dbo.Updates (ObjectId) SELECT TOP (50000) ObjectId = ROW_NUMBER() OVER ( ORDER BY C1.[object_id]) FROM sys.columns AS C1 CROSS JOIN sys.columns AS C2 ORDER BY ObjectId;
測試查詢是:
DECLARE @Rows bigint = 50000; -- Optimized for 1 row, but will be 50,000 when executed SELECT DISTINCT TOP (@Rows) U.ObjectId FROM dbo.Updates AS U WHERE U.UpdateId > 0 OPTION (OPTIMIZE FOR (@Rows = 1));
估計的計劃顯示索引查找和流不同:
輸出當然似乎從以下命令開始:
…但進一步下降的值開始“失去”:
…最終:
在這種特殊情況下的解釋是散列運算符溢出:
一旦分區溢出,散列到同一分區的所有行也會溢出。溢出的分區稍後處理,打破了遇到的不同值將按照接收到的順序立即發出的期望。
有很多方法可以編寫高效的查詢來生成您想要的有序結果,例如遞歸或使用游標。但是,它不能使用Hash Match Flow Distinct來完成。