この質問にはすでに答えがあります:
何も競合しないように追加したにもかかわらず、postgres のシリアルが増加しています
(答えは4つ)
3 年前
に閉店しました。
この質問の重複
次のようなテーブルがあるとします。重複しないようにテーブル内で一意の名前が必要です。物を挿入するプロセスは次のとおりですこの名前のものがすでに存在するかどうかを確認する必要はありません。
CREATE TABLE things(
id SMALLSERIAL PRIMARY KEY,
name varchar UNIQUE
);
このように値を挿入すると機能します。 「desk」がすでにものに含まれている場合は挿入されません。
INSERT INTO things (name)
VALUES ('desk')
ON CONFLICT DO NOTHING;
唯一の問題は、ON CONFLICT DO NOTHING では実際には何も行わないことです。 ID フィールドのシーケンスは引き続きインクリメントされます。
これが頻繁に発生すると、最終的に ID シーケンスがフィールド タイプに対して大きくなりすぎます。
これが起こらないようにする方法はありますか?
あまりにも頻繁ですか?列のデータ型?
– ジャール
2020 年 9 月 3 日 9:11
bigint を使用して、問題を忘れてください。
–
user330315
2020 年 9 月 3 日 9:14
------------------------
競合時に insert ... を使用すると、競合時にシリアルが自動インクリメントされるのを防ぐことはできません。ドキュメントで説明されているように、Postgres は (他のデータベースと同様に) 連続シリアルを保証しません。
smallserial、serial、bigserial はシーケンスを使用して実装されているため、「ホール」が存在する可能性があります。またはシーケンス内のギャップ行が削除されなかった場合でも、列に表示される値は一度も削除されません。シーケンスから割り当てられた値はまだ「使い果たされている」ままです。たとえその値を含む行がテーブル列に正常に挿入されなかったとしてもです。これは、たとえば、挿入トランザクションがロールバックした場合に発生する可能性があります。
大量の挿入を実行して最終的に競合が発生する場合、出血を制限する 1 つの方法は、構文を存在しないように変更することです。
insert into things (name)
select name
from (values ('desk')) v(name)
where not exists (select 1 from things t1 where t1.name = v.name)
これは、シリアルが連続することを保証するものではないことに注意してください (ドキュメントからの上記の引用を参照してください)。