3

私の理解では、WITH ステートメント (CTE) はクエリごとに 1 回実行されます。次のようなクエリを使用します。

WITH Query1 AS ( ... )
SELECT *
FROM
    SomeTable t1
    LEFT JOIN Query1 t2 ON ...

これが100行になる場合、Query1100回ではなく、1回だけ実行されたと思います。この仮定が正しければ、クエリ全体を実行するのにかかる時間は、実行Query1+ から選択するSomeTable+ に結合するSomeTableのにかかる時間とほぼ同じQuery1です。

私は次のような状況にいます:

  • Query1単独で実行すると、約 5 秒 (400k 行) かかります。
  • WITHステートメントを削除した後のクエリの残りの部分と、LEFT JOIN約 15 秒 (400k 行) かかります。

したがって、WITHステートメントとそのLEFT JOIN場でクエリ全体を実行すると、クエリがタイムリーに完了すると予想されていましたが、代わりに1時間以上実行し、停止すると11,000行しか取得できませんでした.

私は明らかに間違っていますが、なぜですか?

4

1 に答える 1

6

例:

SET NOCOUNT ON;
SET IMPLICIT_TRANSACTIONS ON;

CREATE TABLE MyTable (MyID INT  PRIMARY KEY);
GO
INSERT  MyTable (MyID)
VALUES  (11), (22), (33), (44), (55);

PRINT 'Test MyCTE:';
WITH MyCTE
AS (
    SELECT  *, ROW_NUMBER()OVER(ORDER BY MyID) AS RowNum
    FROM    MyTable
)
SELECT  *
FROM    MyCTE crt
LEFT JOIN MyCTE prev ON crt.RowNum=prev.RowNum+1;

ROLLBACK;

SSMSで前のスクリプトを実行すると(Ctrl+M->実際の実行プランを押す)、最後のクエリに対して次の実行プランが 表示されます。ここに画像の説明を入力してください

この場合、CTEはエイリアスに対して1回、crtエイリアスに対して5回(!)実行されprev、からのすべての行に対して1回実行されcrtます。

だから、この質問への答え

WITHステートメントはクエリごとに1回実行されますか、それとも行ごとに1回実行されますか?

both:クエリごとに1crt回()および行ごとに1prev回(:fromごとに1回crt)です。

このクエリを最適化するには、まず、1)CTE(MyCTEまたはQuery)からの結果をテーブル変数または一時テーブルに保存してみます。

2)このテーブルの主キーを結合列として定義します。

3)このテーブル変数または一時テーブルを使用するように最終クエリを書き直します。

もちろん、CTE間のこの自己結合なしで、最終的なクエリを書き直してみることができます。

于 2013-02-08T00:21:46.840 に答える