一連の具体化されたビューを熱心に同期するためのカスタム ワークフローを作成して自動化しました。いくつかの異なるアプローチ (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
これは単一のクエリに固有のものではなく、一般的な質問であるため、結果は含めていません。保守性と健全性のために、このプロジェクトは一貫性を保つ必要があり、この手法はさまざまな構造とユースケースのテーブルで数回使用されます (読み取り負荷が高いものもあれば、書き込み負荷が高いものもあります)。