rub*_*bik 9 postgresql primary-key sequence gaps-and-islands
我id serial PRIMARY KEY
在 PostgreSQL 表中有一个列。id
因为我删除了相应的行,所以缺少许多s。
现在我想通过重新启动序列并以保留id
原始id
顺序的方式重新分配s来“压缩”表。是否可以?
例子:
id | data
----+-------
1 | hello
2 | world
4 | foo
5 | bar
Run Code Online (Sandbox Code Playgroud)
id | data
----+-------
1 | hello
2 | world
3 | foo
4 | bar
Run Code Online (Sandbox Code Playgroud)
我尝试了 StackOverflow answer 中建议的内容,但没有奏效:
# 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.
Run Code Online (Sandbox Code Playgroud)
首先,序列中的间隙是可以预料的。问问自己是否真的需要删除它们。如果你只是忍受它,你的生活就会变得更简单。要获得无间隙数字,(通常更好)的替代方法是使用VIEW
with row_number()
。此相关答案中的示例:
这里有一些消除间隙的方法。
避免了独特的违规和表膨胀带来的并发症,并且速度很快。仅适用于不受 FK 引用、表或其他依赖对象上的视图或并发访问约束的简单情况。在一笔交易中做到,以避免发生意外:
BEGIN;
LOCK tbl; -- optionally: IN SHARE MODE to allow concurrent reads
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;
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL)
复制结构,包括。原始表中的约束和默认值。然后使新表列拥有序列:
并将其重置为新的最大值:
这带来了新表不会膨胀并且聚集在 上的优点id
。
UPDATE
到位这会产生很多死行,并且VACUUM
稍后需要(自动)。
如果该serial
列也是PRIMARY KEY
(如您的情况)或具有UNIQUE
约束,则您必须避免流程中的唯一违规。PK / UNIQUE 约束的(更便宜的)默认值是NOT DEFERRABLE
,这会强制在每一行之后进行检查。此相关问题下的所有详细信息:
您可以将约束定义为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;
Run Code Online (Sandbox Code Playgroud)
当您有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;
Run Code Online (Sandbox Code Playgroud)
缺点如上所述。
TRUNCATE
,,INSERT
如果您有足够的 RAM,还有一种选择。这结合了前两种方式的一些优点。几乎和选项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;
Run Code Online (Sandbox Code Playgroud)
相关,有更多细节:
归档时间: |
|
查看次数: |
2590 次 |
最近记录: |