PostgreSQL-禁用约束

PostgreSQL-禁用约束,postgresql,constraints,Postgresql,Constraints,我有一个大约有500万行的表,其中有一个fk约束引用另一个表的主键(也有大约500万行) 我需要从两个表中删除大约75000行。我知道,如果我尝试在启用fk约束的情况下执行此操作,将花费不可接受的时间 来自Oracle的背景,我的第一个想法是禁用约束,执行删除,然后重新启用约束。如果我是超级用户(我不是,但我以拥有/创建对象的用户身份登录),PostGres似乎允许我禁用约束触发器,但这似乎不是我想要的 另一个选项是删除约束,然后将其恢复。考虑到我的表的大小,我担心重建约束会花费很多时间 有什么

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

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

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

另一个选项是删除约束,然后将其恢复。考虑到我的表的大小,我担心重建约束会花费很多时间

有什么想法吗

编辑:在Billy的鼓励下,我试着在不改变任何约束条件的情况下进行删除,并且耗时超过10分钟。但是,我发现我试图从中删除的表有一个自引用外键。。。重复(&未索引)


最终更新-我删除了自引用外键,删除并重新添加。比利是对的,但不幸的是,我不能接受他的评论作为答案

根据之前的评论,这应该是个问题。也就是说,有一个命令可能就是您想要的-它将约束设置为deferred,以便在提交时检查约束,而不是在每次删除时检查约束。如果你只是对所有的行进行一次大的删除,这不会有什么区别,但是如果你是分块进行的,它会有区别

SET CONSTRAINTS ALL DEFERRED
就是你在那种情况下要找的。请注意,必须将约束标记为
可延迟
,然后才能延迟约束。例如:

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;
TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];

禁用所有表约束

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
--启用所有表约束

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
(此答案假设您的目的是删除这些表的所有行,而不仅仅是选择。)

我也必须这样做,但作为测试套件的一部分。我找到了答案,他建议道。使用方法如下:

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;
TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];
只要引用在列出的表之间,PostgreSQL将删除所有行,而不考虑引用完整性。如果列表以外的表引用了其中一个表的行,则查询将失败

但是,您可以限定查询,以便它还截断所有引用列出的表的表(尽管我没有尝试此操作):

默认情况下,这些表的序列不会重新开始编号。新行将以序列的下一个编号继续。要重新开始序列编号,请执行以下操作:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;

对我来说,有效的方法是逐个禁用将要参与
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在评论部分所述,您需要对表拥有
管理员
权限才能执行此任务。

如果您尝试
禁用所有触发器
,并得到一个类似
权限被拒绝的错误:“RI_ConstraintTrigger_a_16428”是一个系统触发器
(我在Amazon RDS上得到的),请尝试以下操作:

set session_replication_role to replica;
如果此操作成功,则表约束下的所有触发器都将被禁用。现在由您来确保更改使数据库保持一致状态

完成后,可重新启用会话的触发器和约束:

set session_replication_role to default;

我的PostgreSQL是9.6.8

set session_replication_role to replica;
为我工作,但我需要许可

我使用超级用户登录psql

sudo -u postgres psql
然后连接到我的数据库

\c myDB
并运行:

set session_replication_role to replica;

现在我可以使用约束从表中删除。

如果需要这么长的时间,即使有500万行,那么您的设置也有问题。什么?删除还是重新启用约束?是的,很有可能是某些设置错误或未经优化-数据库基本上是由hibernate“构建”的(我与此无关)。删除。从索引表进行FK检查需要线性时间,删除75000+75000行=150000行。考虑每FK检查(二进制搜索,LG(500万)=19)的最坏情况19比较,以及每行比较20个机器比较,等于57 000 000比较。考虑到一个保守的估计,平均每秒钟可以进行十亿次比较,这很容易,但仍然需要不到一秒钟的CPU时间。从磁盘加载也不应该是一个大问题,因为即使在500万行的情况下,表也应该适合RAM。好的,比利-我会再直接删除一次。。。我敢肯定,当我最后一次尝试它时(这是我在一个月左右后重新开始的工作),它非常慢。当然值得一试,但我不相信延迟约束会更快。好吧,他们只是把验证工作从删除时间转移到提交时间。我本来可以尝试一下,但放弃fk并恢复它是有效的。像intgr一样,我想知道它是否会改变fk对提交时间的检查,这样我下次肯定会记住它。我删除了一个数据库,并在运行
SET CONSTRAINTS ALL DEFERRED
后重新导入了它。导入完成后,是否有办法“重新启用”这些约束?这是一个相当大的文件,因此很难对表的创建重新排序。以前我通过导入数据两次来解决这个问题。我从未尝试过这样做,但我尝试过删除约束,然后再重新添加约束,这比删除行时保留约束要快得多。问题是关于Postgresql,它没有这种功能(从v9.4开始)。同意v9.4没有这个功能错误:语法错误在或接近于“NOCHECK”第1行:ALTER TableName NOCHECK CONSTRAINT约束注意