9

大きなテーブルから行のサブセットを返すためにCTEで開始するテーブル値UDFを作成しました。CTEにはいくつかの結合があります。内側の2つと左側の1つは、多くの行を含まない他のテーブルに結合します。CTEには、必要な行のみを返すために、日付範囲内の行を返すwhere句があります。

次に、さまざまな基準を使用して小計を作成するために、このCTEを4つの自己左結合で参照しています。

クエリは非常に複雑ですが、ここにその簡略化された疑似バージョンがあります

WITH DataCTE as
(
     SELECT [columns] FROM table
                      INNER JOIN table2
                      ON [...]

                      INNER JOIN table3
                      ON [...]

                      LEFT JOIN table3
                      ON [...]
)
SELECT [aggregates_columns of each subset] FROM DataCTE Main
LEFT JOIN DataCTE BananasSubset
               ON [...] 
             AND Product = 'Bananas'
             AND Quality = 100
LEFT JOIN DataCTE DamagedBananasSubset
               ON [...]
             AND Product = 'Bananas'
             AND Quality < 20
LEFT JOIN DataCTE MangosSubset
               ON [...]
GROUP BY [

SQL Serverが混乱し、自己結合ごとにCTEを呼び出すように感じます。これは、実行プランを見ると確認できたようですが、それらを読むのは専門家ではないと自白しています。

SQL Serverは、CTEからのデータ取得を数回ではなく、1回だけ実行するのに十分スマートであると想定していました。

同じアプローチを試しましたが、CTEを使用してデータのサブセットを取得するのではなく、CTEと同じselectクエリを使用しましたが、代わりに一時テーブルに出力しました。

CTEバージョンを参照するバージョンは40秒かかります。一時テーブルを参照するバージョンには、1〜2秒かかります。

SQL ServerがCTEの結果をメモリに保持するのに十分スマートでないのはなぜですか?

特にこの場合、UDFはテーブル値であるため、CTEが好きです。そのため、すべてを1つのステートメントにまとめることができました。

一時テーブルを使用するには、UDF値のマルチステートメントテーブルを作成する必要がありますが、これは少し洗練されていないソリューションです。

CTEでこの種のパフォーマンスの問題が発生した方もいらっしゃいましたか?もしそうなら、どのようにしてそれらを分類しましたか?

ありがとう、

カルロス

4

1 に答える 1

7

CTEの結果は毎回取得されると思います。一時テーブルを使用すると、結果はドロップされるまで保存されます。これは、一時テーブルに切り替えたときに見たパフォーマンスの向上を説明しているように思われます。

もう1つの利点は、cteに対しては実行できない一時テーブルにインデックスを作成できることです。あなたの状況に利益があるかどうかはわかりませんが、知っておくのは良いことです。

関連読書:

最後のリンクからの引用:

CTEの基になるクエリは、直後のクエリで参照されるたびに呼び出されます。

私は臨時雇用者のテーブルで行くと思います。残念ながら、エレガントが常に最良の解決策であるとは限りません。

アップデート:

うーん、それは物事をより難しくします。あなたの環境全体を見ずに言うのは難しいです。

いくつかの考え:

  • UDFの代わりに(内部からではなく)ストアドプロシージャを使用できますか?
  • これは不可能な場合がありますが、left joinCTEを削除できる場合は、それをインデックス付きビューに移動できます。これを実行できる場合は、一時テーブルよりもパフォーマンスが向上する可能性があります。
于 2010-06-16T15:45:53.393 に答える