id serial PRIMARY KEY
PostgreSQLテーブルの列。対応する行を削除したため、多くのid
sが欠落しています。
次に、シーケンスを再起動し、元のid
の順序が維持されるようにid
sを再割り当てすることで、テーブルを「圧縮」したいと思います。出来ますか?
例:
id | data
----+-------
1 | hello
2 | world
4 | foo
5 | bar
id | data
----+-------
1 | hello
2 | world
3 | foo
4 | bar
StackOverflowの回答 で提案されたものを試しましたが、うまくいきませんでした:
# alter sequence t_id_seq restart;
ALTER SEQUENCE
# update t set id=default;
ERROR: duplicate key value violates unique constraint t_pkey
DETAIL: Key (id)=(1) already exists.
まず、シーケンスのギャップが予想されます。それらを本当に削除する必要があるかどうか自問してください。あなたがそれと一緒に暮らすなら、あなたの人生はより単純になります。ギャップのない数値を取得するには、(多くの場合)VIEW
をrow_number()
と一緒に使用します。この関連する回答の例:
ここにギャップを取り除くためのいくつかのレシピがあります。
一意の違反とテーブルの膨張による複雑化を回避し、高速です。 の単純な場合のみFK参照、テーブルのビュー、またはその他のビューに拘束されないオブジェクト、または同時アクセス。事故を避けるためにoneトランザクションでそれを行います:
_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
でクラスター化されるという利点があります。
UPDATE
が適切に配置されているこれにより多くのデッド行が生成され、後で(auto-)VACUUM
が必要になります。
serial
column も_PRIMARY KEY
_(あなたの場合のように)であるか、 UNIQUE
制約では、プロセス内で一意の違反を回避する必要があります。 PK/UNIQUE制約の(安価な)デフォルトは_NOT DEFERRABLE
_で、すべての単一行の後にチェックを強制します。 SOに関するこの関連質問の下のすべての詳細:
制約をDEFERRABLE
として定義することができます(これにより、コストが高くなります)。
Or制約を削除し、完了したら再び追加できます。
_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。を参照)。または、競合を回避するために、2番目の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;
_
上記の欠点。
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;
_
関連、詳細付き: