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

Postgresで一時テーブルを使用するよりも遅いCTEを使用して削除する

    CTEは、変更せずに(CTEスキャンを介して)実行する必要があるため、低速です。

    TFM(セクション7.8.2)は次のように述べています。WITHのデータ変更ステートメントが実行されますプライマリクエリが出力のすべて(または実際にはいずれか)を読み取るかどうかに関係なく、正確に1回、常に完了します。これは、WITHのSELECTのルールとは異なることに注意してください。前のセクションで説明したように、SELECTの実行は次のようになります。プライマリクエリがその出力を要求する場合にのみ実行されます。

    したがって、これは最適化の障壁です。;オプティマイザーの場合、同じ結果でよりスマートな計画が得られたとしても、CTEを解体することは許可されていません。

    ただし、CTEソリューションは結合されたサブクエリにリファクタリングできます(質問の一時テーブルと同様)。 postgresでは、結合されたサブクエリは通常、EXISTS()バリアントよりも高速です。

    DELETE FROM customer del
    USING ( SELECT id
            , row_number() over(partition by uuid order by created_date desc)
                     as rn
            FROM customer
            ) sub
    WHERE sub.id = del.id
    AND sub.rn > 1
            ;
    

    もう1つの方法は、TEMP VIEWを使用することです。 。これは構文的に temp tableと同等 ケースですが、意味論的に 結合されたサブクエリフォームと同等です(正確に 少なくともこの場合は、同じクエリプラン)。これは、Postgresのオプティマイザーが解体するためです。 ビューを表示し、メインクエリと組み合わせます(プルアップ )。 viewが表示されます PGの一種のマクロとして。

    CREATE TEMP VIEW targets
    AS SELECT id
            , row_number() over(partition by uuid ORDER BY created_date DESC) AS rn
    FROM customer;
    
    EXPLAIN
    DELETE FROM customer
    WHERE id IN ( SELECT id
                FROM targets
                WHERE rn > 1
            );
    

    [更新:CTEを常に実行して完了する必要があることについて間違っていました。これは、データを変更するCTEの場合のみです]



    1. NodeJSとExpressを使用してMySQL接続プールを作成するにはどうすればよいですか?

    2. ArrayListは同じデータを繰り返します

    3. SQL Server 2008 - 簡単な INSERT トリガーの記述のヘルプ

    4. トリガー機能でNEW。*をEXECUTEに渡す方法