AdventureWorksを使用すると、For each Product get any 1 row of its associated SalesOrderDetail
のクエリが表示されます。row_number()よりもはるかに遅いクロス適用(トップ1を選択)
cross apply
を使用すると、14000msかかる。同等のrow_number
バージョンはわずか70ms(200倍高速)です。
cross apply
も、121317行を返すすべての商品とSalesOrderDetailsの単純なinner join
(v1の場合は266行)と比較して遅いです。
row_number
よりもきれいなので、この種のクエリではcross apply
という構文が好きです。しかし、明らかにcross apply
バージョンは非常に非効率な実行計画を使用しており、使用するには時間がかかりすぎます。
クエリが意図したとおりに動作していないようです。この単純なクエリを実行するのに14秒かかることはありません。他のケースではcross apply
を使用しましたが、これほど遅いことはありませんでした。私の質問は:クエリオプティマイザを混乱させるこの特定のクエリはどうですか?最適な実行計画を使用するのに役立つクエリヒントはありますか? @pacreelyが提案したように、私は各クエリの統計を追加しました。 SalesOrderDetail.LineTotalなし
--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
は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のパフォーマンスに合わせてクロスバージョンを書き直す方法はありますか?" –
私の研究から、これはクロス・アプライが使用されている典型的な方法の1つのようです。しかし、明らかに、内部結合さえもはるかに速いときには、何かが本当に間違っています。根本的な問題を理解したいので、他のクエリにクロス・アプリケーションを使用するときに同様の落とし穴を避けることができます。 –