まず、シーケンスのギャップが予想されます。それらを本当に削除する必要があるかどうか自問してください。一緒に暮らすだけで人生はもっとシンプルになります。ギャップのない数値を取得するには、(多くの場合)代わりにVIEW
with を使用しrow_number()
ます。この関連する回答の例:
ここにギャップを取り除くためのいくつかのレシピがあります。
1.新しい、元のテーブル
独自の違反やテーブルの膨張による複雑化を回避し、高速です。FK参照、テーブルまたはその他の依存オブジェクトのビュー、または同時アクセスによってバインドされていない単純な場合のみ。事故を避けるために、それを1つのトランザクションで実行してください。
BEGIN;
LOCK tbl;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL);
INSERT INTO tbl_new -- no target list in this case
SELECT row_number() OVER (ORDER BY id), data -- all columns in default order
FROM tbl;
ALTER SEQUENCE tbl_id_seq OWNED BY tbl_new.id; -- make new table own sequence
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL)
構造体を含む構造をコピーします。元のテーブルの制約とデフォルト。次に、新しいテーブル列がシーケンスを所有するようにします。
そして、それを新しい最大値にリセットします。
これには、新しいテーブルが膨張せず、でクラスタ化されるという利点がありますid
。
2. UPDATE
所定の場所
これにより、多くのデッド行が生成され、VACUUM
後で(auto-)が必要になります。
場合はserial
、列がもあるPRIMARY KEY
(あなたのケースのように)、または持っているUNIQUE
制約を、あなたは避けなければならないユニークな違反プロセスでは。PK / UNIQUE制約の(安い)デフォルトはですNOT DEFERRABLE
。これは、すべての単一行の後にチェックを強制します。SOに関するこの関連質問の下のすべての詳細:
制約を次のように定義することができますDEFERRABLE
(これにより、コストが高くなります)。
または、制約を削除して、完了したら追加し直すこともできます。
BEGIN;
LOCK tbl;
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
UPDATE tbl t -- intermediate unique violations are ignored now
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
COMMIT;
FOREIGN KEY
(ドキュメントごとに)次の理由により、列を参照する制約がある場合はどちらも不可能です。
参照される列は、参照されるテーブル内の遅延できない一意または主キー制約の列である必要があります。
(関連するすべてのテーブルをロックして)FK制約を削除/再作成し、すべてのFK値を手動で更新する必要があります(オプション3を参照)。またはUPDATE
、競合を回避するために、秒単位で値を移動する必要があります。たとえば、負の数がないと仮定します。
BEGIN;
LOCK tbl;
UPDATE tbl SET id = id * -1; -- avoid conflicts
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id DESC) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
上記の欠点。
3.一時テーブルTRUNCATE
、INSERT
RAMが十分にある場合のもう1つのオプション。これは、最初の2つの方法のいくつかの利点を組み合わせたものです。オプション1とほぼ同じ速さで、膨らみのない手付かずの新しいテーブルが得られますが、オプション2と同様に、すべての制約と依存関係がそのまま保持されます。
ただし、ドキュメントごとに:
TRUNCATE
他のテーブルからの外部キー参照を持つテーブルでは、そのようなテーブルもすべて同じコマンドで切り捨てられない限り、使用できません。そのような場合に有効性をチェックするには、テーブルスキャンが必要です。
大胆な強調鉱山。
FK制約を一時的に削除し、データ変更CTEを使用してすべてのFK列を更新できます。
SET temp_buffers = 500MB; -- example value, see 1st link below
BEGIN;
CREATE TEMP TABLE tbl_tmp AS
SELECT row_number() OVER (ORDER BY id) AS new_id, *
FROM tbl
ORDER BY id; -- order here to use index (if one exists)
-- drop FK constraints in other tables referencing this one
-- which takes out an exclusive lock on those tables
TRUNCATE tbl;
INSERT INTO tbl
SELECT new_id, data -- list all columns in order
FROM tbl_tmp; -- rely on established order in tbl_tmp
-- ORDER BY id; -- only to be absolutely sure (not necessary)
-- example for table "fk_tbl" with FK column "fk_id"
UPDATE fk_tbl f
SET fk_id = t.new_id -- set to new ID
FROM tbl_tmp t
WHERE f.fk_id = t.id; -- match on old ID
-- add FK constraints in other tables back
COMMIT;
関連、詳細付き:
FOREIGN KEYS
がに設定されている場合CASCADE
、古い主キーをループして、その値を(古い値から新しい値に)インプレースで更新できませんでしたか?基本的に、これはオプション3 であり、を使用せずTRUNCATE tbl
、で置き換えINSERT
、UPDATE
外部キーを手動で更新する必要はありません。