web-dev-qa-db-ja.com

CTEの実行回数

以下のコードのようなcteがある場合。人々はテーブルに対して何回問い合わせを受けますか? 1回だけ呼び出されてメモリに保存されているように見えましたが、実行中のクエリの一部が本来よりも長く実行されているようです。これは、人の表を3回ヒットしているのではないかと思います。

with ctegeneric as (select person from people where person = 'dumb')
Select * from ctegeneric 
UNION ALL
Select * from ctegeneric 
UNION ALL
Select * from ctegeneric 
8
scripter78

構文を脇に置くことは正しくありませんでした(編集前)。

いいえ、メモリに配置されません。

適切な例は結合です。ループ結合で複数回呼び出されます。何度も呼び出される高価なCTEでは、#tempに具体化するのがよいでしょう。

7
paparazzo

SQLでは、CTEは、それが定義されている(1つの)ステートメントでのみ使用/参照できます。また、ステートメントターミネータ(;)を使用することで、ステートメントがどこで終わるかがわかります。

SQL Serverは寛容であり、開発者がこれらのターミネータを配置しないようにすることができます(文句を言う特別な場合を除いて)が、すべてのステートメントの後にこれらを使用することは非常に良い方法です(Microsoftはこれをお勧めします)。それらを配置した場合、コードが3つのステートメントとして解析されることは明らかです。

--- 1st statement starts ---
with ctegeneric as (select person from people where person = 'dumb') 
Select * from ctegeneric ;    -- and ends here

--- 2nd statement starts ---
Select * from ctegeneric ;    -- and ends here

--- 3rd statement starts ---
Select * from ctegeneric ;    -- and ends here

したがって、2番目と3番目のステートメントは実際にはまったく機能せず、エラーが返されます。

INVALID OBJECT NAME: ctegeneric

CTEが作成されたステートメントが終了するとすぐに、CTEを再び参照する機能を失います。

これは一種のようなものです(これも有効な構文ではなく、CTEについて考える別の方法です)。

WITH ctegeneric AS (SELECT person 
                    FROM people 
                    WHERE person = 'dumb')

BEGIN
Select * from ctegeneric ;
END

ただし、UNION/UNION ALLを使用して3つの選択を実行できます。

WITH ctegeneric AS (SELECT person 
                    FROM people 
                    WHERE person = 'dumb')   
Select * from ctegeneric
UNION
Select * from ctegeneric
UNION
Select * from ctegeneric ;
4
MguerraTorres

オプティマイザは送信されたSQLを取得し、クエリプランと呼ばれる一連のアクションに変換します。これを行う際に、SQLと論理的に等価な任意のシーケンスでこれらのアクションを自由に配置できます。これにより、SQLで言及されているオブジェクトが1回、何回もアクセスされるか、まったくアクセスされない場合があります。これは、CTEのオブジェクトに適用されます。

したがって、CTEで言及されているオブジェクトは一度だけアクセスされることが一般的に観察されますが、これが発生する保証はありません。

他のSQLステートメントと同様に、CTEパーツを介して処理されるデータは、そのステートメントの期間中のみ有効です。同じデータが後続のステートメントで参照されている場合は、再度アクセスされます。

4
Michael Green

そのコードは実際には機能しません。 (この回答が投稿された後、op更新されたスクリプト)

CTEは実質的にサブクエリです(再帰をサポートします)。それらは、その特定のコマンドのスコープ内でのみ参照できます。そのため、最初のselect *ステートメントは機能し、次の2つはCTEを見つけられないというエラーを出します。これらが機能するためには、それぞれが参照する別のCTEを作成する必要があります。そして、その状況では、peopleテーブルに3回、クエリごとに1回ヒットします。

これを改善するには、結果を一時テーブルまたはテーブル変数に入れて、それをクエリするだけです。

3
Nic