使用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;
跨應用統計
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.
快速的版本,你有問題嗎?你似乎完全明白你在做什麼。 –
正如我在帖子中所說的,「你能幫我理解爲什麼會發生這種情況嗎?有沒有辦法重寫交叉應用版本以匹配row_number的性能?」 –
從我的研究來看,這似乎是交叉應用的典型方法之一。但是,即使內連接速度更快,但顯然這裏確實是錯誤的。我想了解潛在的問題,以便在使用交叉應用進行其他查詢時避免類似的缺陷。 –