2017-02-25 10 views
0

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; 

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

私の研究から、これはクロス・アプライが使用されている典型的な方法の1つのようです。しかし、明らかに、内部結合さえもはるかに速いときには、何かが本当に間違っています。根本的な問題を理解したいので、他のクエリにクロス・アプリケーションを使用するときに同様の落とし穴を避けることができます。 –

答えて

0

ご協力いただきありがとうございます。pacreelyが示唆したように、私はLineTotalが実際に計算された列であることを確認しました。したがって、計算が各行に対して繰り返されるので、すべてを遅くすることは理にかなっています。しかし、LineTotalがなくても上記のように、それはまだ遅すぎます。それで私は、Id以外のすべての列をcross apply節から削除するように導いています。最後に、必要なすべての列を取得するために内部結合を追加しました。このバージョンの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

1つのバッチとして一緒に4文の実行計画のために取得しようと報告された "パーセント記号あなたのタイミングに似ています。古い統計があるかもしれません。

+0

EXEC sp_updatestatsを実行しました。違いはないようです。 –

2

はあなたがCROSSが適用さは、おそらくより多くの読み込み発生していることがわかります

SET STATISTICS IO ON 

でクエリを実行します。 これは、Sales.SalesOrderDetailsテーブルの複数の/重複した読み取りを実行しているためです。

また、RowNumberクエリが「高速」であると仮定しないでください。 SQLは高価なクエリであるため、並列処理を行い、複数のプロセッサを「高速だが高価なリソースにする」と判断しました。 代わりに経過時間のCPU時間で

SET STATISTICS TIME ON 

表情であなたのクエリを実行し、これはあなたのクエリの真の速度が得られます。

各クエリの実行計画を見ると、Selectコンポーネントの詳細が調べられます。クエリには合計コストの尺度があります。コストがサーバーの最大並列度(DBAがDBAを変更しない限り、デフォルトは5)より大きい場合、sqlは経過時間を改善するための並列クエリ計画を作成します。

+0

あなたが提案したように統計を追加しました。あなたは、十字架適用がより多くの読み方をしているということは間違いありません。しかし、CPU時間は各クエリの経過時間に似ています。私は、クエリ・オプティマイザが、cross applyとrow_numberの両方に対して同様の実行計画を生成することを期待していました。特に、このタイプのクロスクエリは多くの例で推奨されています(SOを含む)。だから私は混乱しています、このようにしてクロス・アプライを使用するとは思わないでしょうか? –

+0

残念なことに正解はなく、すべてが "それに依存する"ということになり、すべての環境が異なります。私たちができることは、オプションを試して、結果をどう評価するかを正しく理解することだけです。ユーザーの応答時間が重要なため、Web開発者は並列処理が必要になります。時間外のプロセスでは、より優先度の高い競合するプロセスが存在するため、実行時間が長くなると満足できます。 – pacreely

+0

推定される行数は、1つのクエリにつき非常に異なります。あなたの統計はおそらく古くなっています。インデックスを再構築してみてください。 – pacreely

関連する問題