PostgreSQLでのシーケンスの圧縮


9

私が持っているid serial PRIMARY KEYPostgreSQLのテーブルの列を。id対応する行を削除したため、多くのが欠落しています。

ここで、シーケンスを再起動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.

回答:


9

まず、シーケンスのギャップが予想されます。それらを本当に削除する必要があるかどうか自問してください。一緒に暮らすだけで人生はもっとシンプルになります。ギャップのない数値を取得するには、(多くの場合)代わりにVIEWwith を使用し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.一時テーブルTRUNCATEINSERT

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、で置き換えINSERTUPDATE外部キーを手動で更新する必要はありません。
ギリ

@ギリ:できます、そのようなループは非常に高価です。インデックス内の一意のキー違反により、テーブル全体を一度に更新することはできないUPDATEため、行ごとに個別のコマンドが必要です。②の説明をご覧いただくか、ご自身でお試しください。
Erwin Brandstetter 2017

私の場合、パフォーマンスは問題ではないと思います。私の見方では、「世界を止める」アルゴリズムと、サーバーを停止することなくバックグラウンドで静かに実行するアルゴリズムの2種類があります。ブルームーンで1回だけ圧縮が発生すると(たとえば、データ型の上限に近づくとき)、実際にかかる時間に上限はありません。新しいレコードが追加されるよりも速くレコードを圧縮している限り、問題はありません。
ギリ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.