2017-02-25 13 views
0

使用AdventureWorks,下面列出的是對For each Product get any 1 row of its associated SalesOrderDetail的查詢。交叉應用(select top 1)比row_number()慢得多

使用cross apply需要14000ms。等效的row_number版本只需要70ms(快200倍)。

cross apply也比所有產品和SalesOrderDetails的簡單inner join慢,它返回121317行(當限制TOP 1時爲266行)。

我更喜歡這種查詢的cross apply語法,因爲它比row_number版本更乾淨。但顯然cross apply版本正在使用非常低效的執行計劃,並且太慢而無法使用。

在我看來,查詢不按預期工作。運行這個簡單的查詢不需要14秒鐘。我在其他情況下使用了cross apply,從未遇到任何緩慢的事情。我的問題是:這個特殊的查詢是如何混淆查詢優化器?是否有任何查詢提示可用於幫助其使用最佳執行計劃?正如@pacreely所建議的,我爲每個查詢添加了統計信息。

--CROSS APPLY ~14000ms 
SELECT P.ProductID 
     ,P.Name 
     ,P.ProductNumber 
     ,P.Color 
     ,SOD.SalesOrderID 
     ,SOD.UnitPrice 
     ,SOD.UnitPriceDiscount 
     ,SOD.LineTotal 
FROM Production.Product P 
     CROSS APPLY (SELECT TOP 1 
           * 
         FROM  Sales.SalesOrderDetail S 
         WHERE  S.ProductID = P.ProductID) SOD; 

--ROW_NUMBER ~70ms 
SELECT * 
FROM (SELECT P.ProductID 
        ,P.Name 
        ,P.ProductNumber 
        ,P.Color 
        ,SOD.SalesOrderID 
        ,SOD.UnitPrice 
        ,SOD.UnitPriceDiscount 
        ,SOD.LineTotal 
        ,ROW_NUMBER() OVER (PARTITION BY P.ProductID ORDER BY P.ProductID) RowNum 
      FROM  Production.Product P 
        INNER JOIN Sales.SalesOrderDetail SOD ON SOD.ProductID = P.ProductID) X 
WHERE X.RowNum = 1; 

--Simple INNER JOIN ~400ms (121317 rows) 
SELECT P.ProductID 
     ,P.Name 
     ,P.ProductNumber 
     ,P.Color 
     ,SOD.SalesOrderID 
     ,SOD.UnitPrice 
     ,SOD.UnitPriceDiscount 
     ,SOD.LineTotal 
FROM Production.Product P 
     INNER JOIN Sales.SalesOrderDetail SOD ON SOD.ProductID = P.ProductID; 

也許與此相關的問題,cross apply沒有SalesOrderDetail.LineTotal是速度快10倍。

--CROSS APPLY (Without LineTotal) ~1200ms 
SELECT P.ProductID 
     ,P.Name 
     ,P.ProductNumber 
     ,P.Color 
     ,SOD.SalesOrderID 
     ,SOD.SalesOrderDetailID 
     ,SOD.CarrierTrackingNumber 
     ,SOD.OrderQty 
     ,SOD.ProductID 
     ,SOD.SpecialOfferID 
     ,SOD.UnitPrice 
     ,SOD.UnitPriceDiscount 
     ,SOD.rowguid 
     ,SOD.ModifiedDate 
FROM Production.Product P 
     CROSS APPLY (SELECT TOP 1 
           * 
         FROM  Sales.SalesOrderDetail S 
         WHERE  S.ProductID = P.ProductID) SOD; 

enter image description here Execution plans

跨應用統計

SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 
SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 

(266 row(s) affected) 
Table 'SalesOrderDetail'. Scan count 1, logical reads 363114, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Product'. Scan count 1, logical reads 15, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

(1 row(s) affected) 

SQL Server Execution Times: 
    CPU time = 15688 ms, elapsed time = 16397 ms. 
SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 

ROW_NUMBER統計:

SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 
SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 

(266 row(s) affected) 
Table 'Product'. Scan count 9, logical reads 40, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'SalesOrderDetail'. Scan count 9, logical reads 1371, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

(1 row(s) affected) 

SQL Server Execution Times: 
    CPU time = 360 ms, elapsed time = 266 ms. 
SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 0 ms, elapsed time = 0 ms. 
+0

快速的版本,你有問題嗎?你似乎完全明白你在做什麼。 –

+0

正如我在帖子中所說的,「你能幫我理解爲什麼會發生這種情況嗎?有沒有辦法重寫交叉應用版本以匹配row_number的性能?」 –

+0

從我的研究來看,這似乎是交叉應用的典型方法之一。但是,即使內連接速度更快,但顯然這裏確實是錯誤的。我想了解潛在的問題,以便在使用交叉應用進行其他查詢時避免類似的缺陷。 –

回答

0

感謝您的所有建議。正如我暗示的那樣,我檢查並發現LineTotal實際上是一個計算列。所以它是有道理的,它將會減慢每一行的速度,因爲每一行都會重複計算。但是,即使沒有LineTotal,如上所示,它仍然太慢。然後導致我刪除除cross apply條款中的Id之外的所有列。最後,我添加了一個內部聯接來檢索所有需要的列。這將cross apply查詢作爲row_number查詢

SELECT P.ProductID 
     ,P.Name 
     ,P.ProductNumber 
     ,P.Color 
     ,SOD.SalesOrderID 
     ,SOD.UnitPrice 
     ,SOD.UnitPriceDiscount 
     ,SOD.LineTotal 
FROM Production.Product P 
     CROSS APPLY (SELECT TOP 1 
           S.SalesOrderDetailID 
         FROM  Sales.SalesOrderDetail S 
         WHERE  S.ProductID = P.ProductID) SODID 
     INNER JOIN Sales.SalesOrderDetail SOD ON SOD.SalesOrderDetailID = SODID.SalesOrderDetailID 
1

試圖讓四個語句的執行計劃一起作爲一個批次和看看報告的「百分比e相對於批次「對於每一個類似於你的時間。這可能是你有一些過時的統計數據。

+0

我跑EXEC sp_updatestats;似乎沒有區別。 –

2

SET STATISTICS IO ON 

運行查詢您會看到CROSS APPLY可能產生更多的讀取。 這是因爲您正在執行Sales.SalesOrderDetails表的多個/重複讀取

此外,不要假設RowNumber查詢是「更快」。 SQL已經決定它是一個昂貴的查詢,所以它已經並行並且使用了多個處理器「快速但是資源昂貴」。 與

SET STATISTICS TIME ON 

看看CPU時間,而不是過去的時間運行查詢,這會給你查詢的真實速度。

當您查看每個查詢的執行計劃時,請檢查Select組件的詳細信息。有一個查詢的總成本度量。如果成本大於服務器的最大並行度(默認值爲5,除非您的DBA更改了它),那麼sql將生成並行查詢計劃以提高已用時間。

+0

我已經按照你的建議添加了統計信息。你說得對,交叉申請正在做更多的閱讀。但是CPU時間與每個查詢所用的時間相似。我期望查詢優化器爲交叉應用和row_number生成類似的執行計劃。特別是因爲我見過這種類型的交叉應用查詢在很多示例中都被推薦(包括在SO中)。所以我很困惑,我們是不是想用這種方式交叉應用? –

+0

不幸的是沒有正確的答案,這一切都歸結於「它取決於」,每個環境都不同,我們所能做的就是試驗我們的選擇並正確理解如何評估結果。 Web開發人員需要並行性,因爲用戶響應時間很重要。對於非工作時間的流程,您可能會對較長的運行時間感到滿意,因爲有更高優先級的競爭流程。 – pacreely

+0

您的預計行計數每個查詢都有很大不同。您的統計信息可能已過時。嘗試重建索引。 – pacreely