我有一个大约有500万行的表,它有一个fk约束引用另一个表的主键(也大约有500万行)。
我需要从两个表中删除大约75000行。我知道如果我尝试在启用fk约束的情况下执行此操作,则会花费不可接受的时间。
来自Oracle背景我的第一个想法是禁用约束,执行删除&然后重新启用约束。如果我是超级用户,PostGres似乎让我禁用约束触发器(我不是,但我以拥有/创建对象的用户身份登录)但这似乎不是我想要的。
另一种选择是删除约束然后恢复它。考虑到桌子的大小,我担心重建约束会花费很长时间。
有什么想法吗?
编辑:在比利的鼓励之后,我尝试在不改变任何限制的情况下进行删除,并且需要超过10分钟。但是,我发现我试图删除的表有一个自引用外键...重复(& non indexed)。
最后的更新 - 我放弃了自我引用的外键,删除了它并将其添加回来。比利是正确的,但不幸的是我不能接受他的评论作为答案!
答案 0 :(得分:47)
以前的评论,应该是一个问题。也就是说,有一个命令可能是您正在寻找的 - 它会将约束设置为延迟,因此它们在COMMIT上检查,而不是在每次删除时检查。如果你只对所有行进行一次大的删除,那么它就不会有所作为,但是如果你分成几行,它就会。
SET CONSTRAINTS ALL DEFERRED
在这种情况下,你正在寻找。请注意,约束必须标记为DEFERRABLE
才能延迟。例如:
ALTER TABLE table_name
ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2)
DEFERRABLE INITIALLY IMMEDIATE;
然后可以在事务或函数中延迟约束,如下所示:
CREATE OR REPLACE FUNCTION f() RETURNS void AS
$BODY$
BEGIN
SET CONSTRAINTS ALL DEFERRED;
-- Code that temporarily violates the constraint...
-- UPDATE table_name ...
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
答案 1 :(得分:15)
对我有用的是逐个禁用那些将参与TRIGGERS
操作的DELETE
个表。
ALTER TABLE reference DISABLE TRIGGER ALL;
DELETE FROM reference WHERE refered_id > 1;
ALTER TABLE reference ENABLE TRIGGER ALL;
解决方案适用于版本9.3.16。在我的情况下,执行DELETE
操作的时间从45分钟到14秒。
如@amphetamachine的评论部分所述,您需要拥有admin
个权限才能执行此任务。
答案 2 :(得分:4)
(这个答案假设您的意图是删除这些表的所有行,而不仅仅是选择。)
我也必须这样做,但作为测试套件的一部分。我找到了答案,建议elsewhere on SO。使用TRUNCATE TABLE,如下所示:
TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];
以下内容会快速删除表table1
,table2
和table3
中的所有行,前提是没有从未列出的表中引用这些表的行:
TRUNCATE TABLE table1, table2, table3;
只要在列出的表之间引用,PostgreSQL就会删除所有行,而不用考虑参照完整性。如果列出的表之外的表引用其中一个表的行,则查询将失败。
但是,您可以限定查询,以便它还会截断所有表,并引用列出的表(尽管我没有尝试过这个):
TRUNCATE TABLE table1, table2, table3 CASCADE;
默认情况下,这些表的序列不会重新开始编号。新行将继续下一个序列号。要重新启动序列编号:
TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;
答案 3 :(得分:3)
如果您尝试DISABLE TRIGGER ALL
并收到类似permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger
的错误(我在Amazon RDS上获得此信息),请尝试以下操作:
set session_replication_role to replica;
如果成功,将禁用表约束基础的所有触发器。现在由您来确保您的更改使数据库保持一致状态!
然后当你完成后,重新启用触发器&amp;会话的约束:
set session_replication_role to default;
答案 4 :(得分:1)
我的PostgreSQL是9.6.8。
set session_replication_role to replica;
为我工作,但我需要许可。
我以超级用户身份登录psql。
sudo -u postgres psql
然后连接到我的数据库
\c myDB
然后运行:
set session_replication_role to replica;
现在我可以从表中删除约束了。
答案 5 :(得分:-8)
禁用所有表约束
ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
- 启用所有表约束
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName