1

一連の具体化されたビューを熱心に同期するためのカスタム ワークフローを作成して自動化しました。いくつかの異なるアプローチ (1 対多の関係) を試した結果、最も信頼できる同期ワークフローは、影響を受けた可能性のあるすべてのレコードを削除してから、新しいレコードを挿入することであることがわかりました。

DELETE FROM
    some_materialized_view
WHERE
    set_of_records_key = some_value;

INSERT INTO
    some_materialized_view
SELECT
    *
FROM
    some_query_generating_some_materialized_view;

注: some_query_generating_some_materialized_view は、実行にかなりの量のリソースを必要とする複雑な読み取り操作です。さらに、 some_materialized_view は、いくつかの外部キーとその他の制約で大量にインデックス化されています。

これは非常に扱いが重いと感じます。このワークフローには、過剰な削除操作と挿入操作が伴いますが、削除されたレコードの一部が同一であるか、UPDATE の候補になるほど類似している可能性があるため、多くの場合不要です。

私は次のようなものを好むでしょう:

DELETE FROM
    some_materialized_view
USING
    (
        SELECT
            unique_key
        FROM
            some_materialized_view
        WHERE
            set_of_records_key = some_value

        EXCEPT
        INSERT INTO
            some_materialized_view
        SELECT
            *
        FROM
            some_query_generating_some_materialized_view
        ON CONFLICT (...) DO UPDATE
        SET 
            foo = EXCLUDED.foo,
            bar = EXCLUDED.bar,
            ...
        WHERE
            some_materialized_view <> EXCLUDED
        RETURNING
            unique_key
    ) AS sub_query
WHERE
    some_materialized_view.unique_key = sub_query.unique_key;

問題はON CONFLICT ... DO UPDATE ... WHERE ... RETURNING節にあります。

この質問で対処されているように: How to use RETURNING with ON CONFLICT in PostgreSQL?

RETURNING 句は、影響を受けるレコードのみを返します。そのため、影響を受けていないレコードは返されないため、(上記の例では) 不適切に削除されます。

実際にすべてのレコードを返す唯一の方法は、句RETURNINGを削除して同一のレコードを不必要に更新するWHERE some_materialized_view <> EXCLUDEDか、別の句で some_query_generating_some_materialized_view を再度実行することEXCEPTです...両方のオプションも理想的ではありません。

それで、私は何が欠けていますか?他に利用可能なオプションはありますか? そうでない場合、一般に、不要な UPDATE よりも複雑でリソースを大量に消費する読み取り操作を実行することをお勧めします (関連するインデックスのメンテナンスとチェックの制約を思い出してください)。

注:EXPLAIN ANALYZEこれは単一のクエリに固有のものではなく、一般的な質問であるため、結果は含めていません。保守性と健全性のために、このプロジェクトは一貫性を保つ必要があり、この手法はさまざまな構造とユースケースのテーブルで数回使用されます (読み取り負荷が高いものもあれば、書き込み負荷が高いものもあります)。

4

1 に答える 1

0
  • 疑似コード (疑似コードは好きではない)
  • 連鎖 CTE のデモンストレーション
  • ロック、シリアル化、およびレースを無視します。
  • (および質問のセマンティクス/コンテキスト)

WITH fresh AS ( -- workhorse: Called only and exactly once
        SELECT <keyfields> -- *
        FROM some_query_generating_some_materialized_view
        )
,upd AS ( -- update existing rows
        UPDATE some_materialized_view mv
        SET foo = fr.foo, bar = fr.bar
        FROM fresh fr
        WHERE mv.<keyfields = fr.<keyfields>
        RETURNING mv.<keyfields>
        )
/* 
, del AS ( 
        no deletes ???
        )
 */
        -- insert non existing rows.
INSERT INTO some_materialized_view mv ( <targetfields> )
SELECT fr.<srcfields>
FROM fresh fr
WHERE NOT EXISTS (
        SELECT *
        FROM upd nx
        WHERE nx.<keyfields> = fr.<keyfields>
        );
于 2016-12-27T18:24:38.043 に答える