Common Table Expression(CTE)の利点は?


21

msdnから:

派生テーブルとは異なり、CTEは自己参照することができ、同じクエリで複数回参照できます。

私はCTEをかなり頻繁に使用していますが、CTEを使用する利点について深く考えたことはありません。

同じクエリでCTEを複数回参照する場合:

  • パフォーマンス上の利点はありますか?
  • 自己結合を実行している場合、SQL Serverはターゲットテーブルを2回スキャンしますか?

2
プロファイラーは、2回スキャンするかどうかを通知する必要があります。私見、CTEは再帰に最適です。
ダンアンドリュース

3
クエリオプティマイザーが機能しているとき、難しい答えはありません。一部のクエリではパフォーマンス上の利点が見られますが、そうでないものもあります。CTEの代わりに一時テーブルを使用する方が高速な場合もあれば、そうでない場合もあります。

回答:


25

原則として、CTEはパフォーマンスを決して改善しません

CTEは本質的に使い捨てのビューです。追加の統計情報やインデックスなどは保存されていません。サブクエリの短縮形として機能します。

私の意見では、それらは簡単に使い古される可能性があります(私の仕事ではコードに多くの使いすぎが見られます)。 いくつかの良い答えはここにありますが、何かを複数回参照する必要がある場合、または数十万行を超える場合は、#temp代わりにテーブルに入れてインデックスを作成します。


3
同意する。再帰的なCTEを除き、読みやすさを支援するだけです
-gbn

CTEが数行(メモリ内に保持できる)のみを返し、計算に大きなコストがかかり(大きなテーブルでの集計)、その結果が複数回使用された場合はどうなりますか?これでパフォーマンスが向上するはずです。(少なくとも、一時テーブルがほとんど使用されないPostgreSQLとOracleでの私の経験です)
a_horse_with_no_name

2
@a_horse_with_no_name-単にサブクエリにすることと同等です。1つのクエリで結果が複数回使用された場合、結果は再利用され、再計算されません。複数のクエリで使用される場合CTE、最初のクエリの後に結果が破棄されるため、a は不適切な選択です。
JNK

@JNK:ありがとう。ここでは、SQL Serverの動作が異なるようです。
-a_horse_with_no_name

特定の状況でCTEの方が読みやすいと感じる人もいますFWIW stackoverflow.com/a/11170918/32453
rogerdpack

14

再帰以外にCTEが非常に役立つと思う場所の1つは、複雑なレポートクエリを作成するときです。一連のCTEを使用して必要なデータのチャンクを取得し、最終選択で結合します。多数の派生テーブルや20の結合で同じことを行うよりも保守が容易であることがわかります。また、すべての異なる結合。簡単な例を挙げましょう。

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

したがって、必要な情報のさまざまなチャンクを分離することにより、各部分を個別にチェックできます(コメントアウトされた選択を使用して、各コメントを個別にアンコメントし、その選択範囲までのみ実行します)および費用を変更する必要がある場合計算(この例)では、それらがすべて1つの大規模なクエリに混在している場合よりも簡単に見つけることができます。もちろん、これを使用する実際のレポートクエリは、一般に例よりもはるかに複雑です。


1
クエリを報告するだけですか?私が毎日作業しているシステムには、それほど複雑なトランザクションクエリがあります。奇妙なことに、レポートクエリは多くの場合、単純なクエリです。(もちろん、つまらないジョインレスCRUDクエリを除外します)。
ケビンキャスカート

ここではそれらが通常最も複雑であるため、これを例として使用しました
-HLGEM

+1場合によっては、パフォーマンスが向上する可能性のあるクエリよりも論理的な(人間が読み取れる)クエリの方が望ましい場合があります。
いつか

はい。通常、CTEが同じ結果プランを生成することを考えると、恐ろしくネストされた複数サブクエリの怪物を作成する理由はありません。代わりに、必要な順序で各コンポーネントを視覚的にレイアウトできます。XMLファイルをインポートし、さまざまなアクロバットを実行して、データを正しい形式にします。これは、CTEなしでは書き込み/読み取りには耐えられません。(私の古いコードのいくつかは恐ろしいサブクエリを持っているかもしれません!)
underscore_d

0

常に依存しますが、パフォーマンスが大幅に改善される場合があります。選択にCTEを使用し、それをINSERT INTOで使用するINSERT INTO SELECTステートメントでそれを確認します。データベースに対してRCSIがオンに設定されていることと関係があるかもしれませんが、選択されているものが非常に少ない場合は、かなり役立ちます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.