PostgreSQL - 禁用约束

时间:2010-04-21 02:04:33

标签: postgresql constraints

我有一个大约有500万行的表,它有一个fk约束引用另一个表的主键(也大约有500万行)。

我需要从两个表中删除大约75000行。我知道如果我尝试在启用fk约束的情况下执行此操作,则会花费不可接受的时间。

来自Oracle背景我的第一个想法是禁用约束,执行删除&然后重新启用约束。如果我是超级用户,PostGres似乎让我禁用约束触发器(我不是,但我以拥有/创建对象的用户身份登录)但这似乎不是我想要的。

另一种选择是删除约束然后恢复它。考虑到桌子的大小,我担心重建约束会花费很长时间。

有什么想法吗?

编辑:在比利的鼓励之后,我尝试在不改变任何限制的情况下进行删除,并且需要超过10分钟。但是,我发现我试图删除的表有一个自引用外键...重复(& non indexed)。

最后的更新 - 我放弃了自我引用的外键,删除了它并将其添加回来。比利是正确的,但不幸的是我不能接受他的评论作为答案!

6 个答案:

答案 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];

以下内容会快速删除表table1table2table3中的所有行,前提是没有从未列出的表中引用这些表的行:

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