sql >> データベース >  >> RDS >> Sqlserver

パフォーマンスの高いCTEテーブルと一時テーブルのどちらですか?

    状況によります。

    まず第一に

    共通テーブル式とは何ですか?

    (非再帰的な)CTEは、SQLServerでインラインテーブル式としても使用できる他の構成と非常によく似た方法で処理されます。派生テーブル、ビュー、およびインラインテーブル値関数。 BOLは、CTEは「一時的な結果セットと考えることができる」と述べていますが、これは純粋に論理的な説明であることに注意してください。多くの場合、それ自体は材料化されていません。

    一時テーブルとは何ですか?

    これは、tempdbのデータページに格納されている行のコレクションです。データページは、部分的または全体的にメモリ内に存在する場合があります。さらに、一時テーブルにインデックスが付けられ、列の統計が含まれる場合があります。

    テストデータ

    CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);
    
    INSERT INTO T(B)
    SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
    FROM master..spt_values v1,
         master..spt_values v2;
    

    例1

    WITH CTE1 AS
    (
    SELECT A,
           ABS(B) AS Abs_B,
           F
    FROM T
    )
    SELECT *
    FROM CTE1
    WHERE A = 780
    

    上記の計画には、CTE1についての言及がないことに注意してください。ベーステーブルに直接アクセスするだけで、

    と同じように扱われます。
    SELECT A,
           ABS(B) AS Abs_B,
           F
    FROM   T
    WHERE  A = 780 
    

    ここでCTEを中間の一時テーブルに具体化して書き直すと、生産性が大幅に低下します。

    のCTE定義の具体化
    SELECT A,
           ABS(B) AS Abs_B,
           F
    FROM T
    

    約8GBのデータを一時テーブルにコピーする必要がありますが、それでもそこから選択するオーバーヘッドがあります。

    例2

    WITH CTE2
         AS (SELECT *,
                    ROW_NUMBER() OVER (ORDER BY A) AS RN
             FROM   T
             WHERE  B % 100000 = 0)
    SELECT *
    FROM   CTE2 T1
           CROSS APPLY (SELECT TOP (1) *
                        FROM   CTE2 T2
                        WHERE  T2.A > T1.A
                        ORDER  BY T2.A) CA 
    

    上記の例は、私のマシンでは約4分かかります。

    ランダムに生成された1,000,000個の値のうち15行だけが述語に一致しますが、これらを見つけるために高価なテーブルスキャンが16回行われます。

    これは、中間結果を具体化するための良い候補になります。同等の一時テーブルの書き換えには25秒かかりました。

    INSERT INTO #T
    SELECT *,
           ROW_NUMBER() OVER (ORDER BY A) AS RN
    FROM   T
    WHERE  B % 100000 = 0
    
    SELECT *
    FROM   #T T1
           CROSS APPLY (SELECT TOP (1) *
                        FROM   #T T2
                        WHERE  T2.A > T1.A
                        ORDER  BY T2.A) CA 
    

    クエリの一部を一時テーブルに中間的にマテリアライズすると、一度だけ評価された場合でも、マテリアライズされた結果の統計を利用してクエリの残りの部分を再コンパイルできる場合に役立つことがあります。このアプローチの例は、SQLCatの記事「複雑なクエリを分解する場合」にあります。

    状況によっては、SQLServerはスプールを使用して中間結果をキャッシュします。 CTEの、そしてそのサブツリーを再評価する必要を避けます。これについては、(移行された)接続項目で説明されています。CTEまたは派生テーブルの中間実体化を強制するためのヒントを提供します。ただし、これに関する統計は作成されず、スプール行の数が見積もりと大幅に異なる場合でも、進行中の実行プランがそれに応じて動的に適応することはできません(少なくとも現在のバージョンでは。適応クエリプランはで可能になる可能性があります。未来)。



    1. MariaDBでのRPAD()のしくみ

    2. MySQLクエリでテキストを数値に変換する

    3. Postgresql:MacOSXを使用してpg_hba.confファイルを見つける方法

    4. MySQLでのREGEXP_LIKE()関数のしくみ