次のようなストアドプロシージャがあります。
INSERT INTO schema.my_unique_values
SELECT DISTINCT id, value
FROM schema.a_huge_table
WHERE NOT EXISTS (SELECT 1
FROM schema.my_unique_values)
要約すると、このクエリは、a_huge_tableからmy_unique_valuesテーブルに一意の行を挿入します。
私が抱えている問題は、理由のためにマルチスレッドスクリプトでこのクエリを同時に実行すると、重複する行が挿入される可能性があることです。この「ファントム読み取り」を回避するためにシリアライズ可能を使用しようとしましたが、それでもうまくいきません。私の考えは、Postgresが一意の制約違反に対して重複しない行のみを挿入できるようにすることです。しかし、これは可能ですか?私の現在の経験では、一意の制約違反が発生すると、トランザクション全体がキャンセルされるため、重複しない行は挿入されません。どうすれば目標を達成できますか?
注:Postgres8.2を使用しているGreenplum4.3.11を使用しているため、使用できるクエリには制限があります。
ありがとう..
まず、クエリを少し変更する必要があると思います。これは、現在記述されているWHERE NOT EXISTSは、テーブルに行があるとすぐにfalseになるためです。 WHERE句を指定する必要があります。
_INSERT INTO schema.my_unique_values
SELECT DISTINCT id, value
FROM schema.a_huge_table a
WHERE NOT EXISTS
(SELECT 1
FROM schema.my_unique_values m
WHERE m.id = a.id AND m.value = a.value)
_
INSERT
は、トランザクション内で、または単一ステートメントトランザクションとして、アトミックになります。したがって、1つの行が失敗すると、すべてが失敗します。いずれにせよ、あなたはできませんSERIALIZABLE
トランザクション分離レベル。その分離レベルに達していない場合は、NOT EXISTS (...)
に正しく記述されたWHERE
句があることを確認してください。
PostgreSQLバージョン9.5の時点で、INSERT
の一部となることができ、ユースケースを正確に検索する新しい句があります。 _ON CONFLICT DO NOTHING
_ を利用するようにクエリを変更できます。
_INSERT INTO schema.my_unique_values
SELECT DISTINCT id, value
FROM schema.a_huge_table a
ON CONFLICT DO NOTHING ;
_
注:_ON CONFLICT
_句は、INSERT
全体ではなく、各行に適用されます。
別のアイデアとして、これはキャッシュテーブルです。私はそれらのファンではありません。 (id,value)
がschema.a_huge_table
から削除されるとどうなりますか?
CREATE MATERIALIZED VIEW schema.my_unique_values AS
SELECT DISTINCT id, value
FROM schema.a_huge_table;
それからあなたがそれをリフレッシュしたいとき..
REFRESH MATERIALIZED VIEW schema.my_unique_values
更新することもできますCONCURRENTLY
REFRESH MATERIALIZED VIEW CONCURRENTLY schema.my_unique_values
マテリアライズド・ビューの同時選択をロックアウトせずに、マテリアライズド・ビューをリフレッシュします。このオプションがないと、多くの行に影響を与える更新は、使用するリソースが少なくなり、より迅速に完了する傾向がありますが、マテリアライズド・ビューから読み取ろうとしている他の接続をブロックする可能性があります。影響を受ける行数が少ない場合は、このオプションの方が高速になる可能性があります。
言うまでもなく、それは常に完璧なソリューションです。しかし、私は一般的に、それが合理的な範囲のほとんどのものにとってより良い解決策であると思います。