我的 Postgres 数据库中有一个表,大约有 400 万行,我需要将其减少到大约 30,000 行,同时由于外键引用而保留原始 ID。删除的条件基于名为 is_X 的列,并且应删除该列为 false 的行。注意:我正在使用 Ruby on Rails。
我正在寻找最有效的方法来实现这一目标,而不会导致过度的性能下降和维护中断。以下是我正在考虑的一些选择:
我正在寻求关于哪种方法在执行时间和数据库性能方面最快、最有效的建议。如果还有其他我没有考虑过的替代方法,也请分享。
任何有效完成此任务的见解、最佳实践或代码示例将不胜感激。
由于其效率和潜在缺点的不确定性,我还没有最终确定其中任何一个。
对于给定的基数,通常在列表中总体上最快 - if它实际上是一个选项。您需要
TRUNCATE
表所需的权限。当截断表中的任何行仍然被引用时,提到的 FK 约束会引发异常。
您可能删除 FK 约束,然后重新创建:
BEGIN;
ALTER TABLE other_tbl DROP CONSTRAINT IF EXISTS some_name_fkey;
-- more?
CREATE TEMP TABLE tbl_tmp AS
SELECT * FROM tbl WHERE is_x IS NOT FALSE
-- ORDER BY ?? -- cluster while being at it?
;
TRUNCATE tbl;
INSERT INTO tbl SELECT * FROM tbl_tmp;
ALTER TABLE other_tbl
ADD CONSTRAINT some_name_fkey FOREIGN KEY (col) REFERENCES tbl(col);
-- more?
COMMIT;
如果涉及临时表,请确保
temp_buffers
设置得足够高以在 RAM 中处理它。参见:
但是如果您能做到这一点,您也许可以立即创建一个新表,并删除旧表,跳过临时表。注意其他依赖项,例如视图等。
BEGIN;
ALTER TABLE tbl RENAME TO tbl_old; -- to free up the name
CREATE TABLE tbl AS
SELECT * FROM tbl_old WHERE is_x IS NOT FALSE
-- ORDER BY ??? -- cluster while being at it?
;
-- comments, constraints, indexes etc.?
ALTER TABLE other_tbl
DROP CONSTRAINT some_name_fkey -- drop old
, ADD CONSTRAINT some_name_fkey FOREIGN KEY (col) REFERENCES tbl(col);
-- more?
DROP tbl_old;
COMMIT;
使用 superuser 权限,您甚至可以禁用所有触发器(FK 约束是通过内部生成的约束触发器实现的)。更快,但更危险。与第一个选项不同,当您再次启用触发器时,不会检查引用完整性。所以你最好确切地知道你在做什么,以免弄坏东西。
BEGIN;
ALTER TABLE tbl DISABLE trigger ALL; -- are you sure?
CREATE TEMP TABLE tbl_tmp AS
SELECT * FROM tbl WHERE is_x IS NOT FALSE
-- ORDER BY ??? -- cluster while being at it?
;
TRUNCATE tbl;
INSERT INTO tbl SELECT * FROM tbl_tmp;
ALTER TABLE tbl ENABLE trigger ALL;
COMMIT;
参见:
也就是说,对于几百万行,运行
DELETE
可能是最简单、最安全的。跟进VACUUM ANALYZE tbl
。VACUUM FULL ANALYZE tbl;