Optimization

TOPN 如何影響使用 group by 的查詢?

  • August 17, 2015

我有一個如下所示的查詢:

Select t1.field1, t2.field2, sum(t1.field3)
From  t1 inner join t2 on t1.id = t2.id
Group by t1.field1, t2.field2

它被簡化了,通常它上面還有一些連接和屬性。我現在的問題是,將 TOP N 子句添加到查詢而不是在 N 行返回到我的 C# 程式碼後停止查詢的影響有多大。

我知道,添加該子句對伺服器來說更好,但我對粗略估計該子句的影響感興趣。我的猜測是,因為我有一個聚合,伺服器無論如何都必須處理所有行,因此影響不是那麼高。

編輯:C# Snippet 我們如何停止執行

var cmd = new SqlCommand();
cmd.CommandText = sqlStatement;
cmd.Connection = connectionString;
SqlDataReader dr;
dr = cmd.ExecuteReader();
while (dr.HasRows)
{
   while (dr.Read())
   {
       MyDataRow newRow = new MyDataRowDataRow();
       newRow.Parse(dr); //Parse the value from datarow to our format
       result.Add(newRow);
       if (topN.HasValue)
       {
           if (result.Count >= topN.Value)
           {
               break; //stop reading!
           }
       }
   }
   dr.NextResult();
}

我的猜測是,因為我有一個聚合,伺服器無論如何都必須處理所有行,因此影響不是那麼高。

從 SQL Server 的角度來看,這取決於。以下是它所依賴的內容以及原因的概述:

行目標

添加頂級TOP (n)子句(帶或不帶ORDER BY)具有與指定查詢提示相同的行目標效果。FAST (n)行目標使優化器根據快速返回n行來估計計劃成本。

因此,小行目標傾向於使用非阻塞(流水線)操作符(如(無排序)合併或嵌套循環連接)和半阻塞(每組)操作符(如流聚合)串列執行,而不是完全阻塞操作符(如排序和急切雜湊)清楚的。

如果預計建構輸入很小,並且預計建構輸入不會重新綁定太多(或在全部)。

流水線執行

在查詢邏輯允許並且存在合適索引的情況下,優化器很可能能夠找到完全流水線(或者可能是半阻塞)的執行計劃。每組阻塞的半阻塞計劃可能很好,只要組很小。

即使使用最佳索引,在某些情況下,也可能需要使用正確的語法來表達查詢,也許還需要使用各種查詢和表提示,以獲得盡可能多的流水線執行計劃。除了最簡單的情況外,在所有情況下都很難實現。

流水線/半阻塞計劃意味著SqlDataReader.Read()可以以最小的延遲獲取第一行和後續行。本質上,C# 程式碼和 SQL Server 執行引擎都是流式行。在這種情況下,指定TOP (n)將產生最大的不同。

預設策略

如果TOP (n)未指定,查詢優化器的目標是查詢預期生成的*全部潛在行集的**總執行時間。*例如,優化目標的這種變化傾向於支持並行性、完全阻塞排序和急切雜湊聚合,以及部分阻塞雜湊連接。這通常會比執行流水線/半阻塞計劃(如果可用)到最終結論快得多。

當一個或多個阻塞操作符出現在一個計劃中時,第一行的時間可能是潛在總執行時間的相當大的一部分。這意味著第一次SqlDataReader.Read()呼叫將阻塞很長時間。在這種情況下,在客戶端接收到 n 行後停止 DataReader 將對經過的時間產生相對較小的影響,因為在第一行可用之前,大部分 SQL Server 工作已經完成。

結論

因此,增加多少效果TOP (n)至少取決於:

  • 有一個流水線執行計劃解決方案(即使在原則上)
  • 合適的訪問方法(索引)
  • 查詢語法和優化器功能/限制
  • 查詢開發人員的技能水平和經驗

例子

使用ContosoRetailDW 範例數據集,其中表FactOnlineSales有 12,627,600 行。基於問題中模板的查詢是:

SELECT
   FOS.ProductKey, 
   DS.StoreKey, 
   SUM(FOS.SalesAmount)
FROM dbo.DimStore AS DS
JOIN dbo.FactOnlineSales AS FOS
   ON DS.StoreKey = FOS.StoreKey
GROUP BY 
   FOS.ProductKey,
   DS.StoreKey
ORDER BY
   FOS.ProductKey,
   DS.StoreKey;

添加有用的索引:

CREATE INDEX i 
ON dbo.FactOnlineSales 
   (ProductKey, StoreKey) 
INCLUDE 
   (SalesAmount);

執行計劃是:

預設計劃

這執行1650ms。注意排序和散列操作。

與 TOP (n)

任意選擇 n = 50,查詢現在是:

SELECT TOP (50)
   FOS.ProductKey, 
   DS.StoreKey, 
   SUM(FOS.SalesAmount)
FROM dbo.DimStore AS DS
JOIN dbo.FactOnlineSales AS FOS
   ON DS.StoreKey = FOS.StoreKey
GROUP BY 
   FOS.ProductKey,
   DS.StoreKey
ORDER BY
   FOS.ProductKey,
   DS.StoreKey;

和執行計劃:

流水線計劃

使用流水線迭代器和每組半阻塞流聚合(而不是阻塞急切雜湊)執行 57 毫秒

相關問題:如何(以及為什麼)TOP影響執行計劃?

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