PostgreSQL - 禁用约束

azp*_*p74 44 postgresql constraints

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

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

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

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

有什么想法吗?

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

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

Mag*_*der 50

根据以前的评论,这应该是一个问题.也就是说,有一个命令可能是您正在寻找的 - 它会将约束设置为延迟,因此它们在COMMIT上检查,而不是在每次删除时检查.如果你只对所有行进行一次大的删除操作,那么它就不会有所作为,但是如果你分成几行,那就会.

SET CONSTRAINTS ALL DEFERRED
Run Code Online (Sandbox Code Playgroud)

在这种情况下你正在寻找什么.请注意,约束必须标记为DEFERRABLE可以延迟之前.例如:

ALTER TABLE table_name
  ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2)
  DEFERRABLE INITIALLY IMMEDIATE;
Run Code Online (Sandbox Code Playgroud)

然后可以在事务或函数中延迟约束,如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

  • 当然值得一试,但我不相信延迟约束更快.AFAIK他们只是将验证工作从DELETE-time转移到COMMIT-time. (2认同)
  • 我删除了一个数据库并在运行`SET CONSTRAINTS ALL DEFERRED`后重新导入它.导入完成后,有没有办法"重新启用"这些约束?这是一个非常庞大的文件,因此重新排序表创建将非常困难.我之前通过两次导入数据解决了这个问题. (2认同)

ger*_*cin 15

对我有用的是逐个禁用TRIGGERS那些将参与DELETE操作的表格.

ALTER TABLE reference DISABLE TRIGGER ALL;
DELETE FROM reference WHERE refered_id > 1;
ALTER TABLE reference ENABLE TRIGGER ALL;
Run Code Online (Sandbox Code Playgroud)

解决方案适用于版本9.3.16.在我的情况下,时间从45分钟到14秒执行DELETE操作.

如@amphetamachine的评论部分所述,您需要拥有admin表的权限才能执行此任务.

  • 这并没有禁用“UNIQUE”验证,否则我做错了什么(也许“UNIQUE”约束没有作为“TRIGGER”进行管理) (3认同)
  • @TheRedPea:也许这是一个不可延迟的唯一约束...此页面有更多信息:[如何禁用我的 postgresql 中的所有约束?](https://entityframeworkcore.com/knowledge-base/56335488/how-can -i-disable-all-contraints-in-my-postgresql-) :“...不可延迟的主键、唯一和排除约束没有关联的触发器,并且不受影响。” (3认同)

小智 6

我的 PostgreSQL 是 9.6.8。

set session_replication_role to replica;
Run Code Online (Sandbox Code Playgroud)

为我工作,但我需要许可。

我用超级用户登录psql。

sudo -u postgres psql
Run Code Online (Sandbox Code Playgroud)

然后连接到我的数据库

\c myDB
Run Code Online (Sandbox Code Playgroud)

并运行:

set session_replication_role to replica;
Run Code Online (Sandbox Code Playgroud)

现在我可以从带有约束的表中删除。


Joe*_*app 5

(此答案假定您的意图是删除这些表的所有行,而不仅仅是选择。)

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

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];
Run Code Online (Sandbox Code Playgroud)

如果没有从未列出的表中引用这些表的行,以下内容会快速删除表table1table2和 中的所有行table3

TRUNCATE TABLE table1, table2, table3;
Run Code Online (Sandbox Code Playgroud)

只要引用在列出的表之间,PostgreSQL 就会删除所有行而不考虑引用完整性。如果未列出的表引用这些表之一的行,则查询将失败。

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

TRUNCATE TABLE table1, table2, table3 CASCADE;
Run Code Online (Sandbox Code Playgroud)

默认情况下,这些表的序列不会重新编号。新行将继续使用序列的下一个编号。要重新启动序列编号:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;
Run Code Online (Sandbox Code Playgroud)


Jon*_*rth 5

如果尝试DISABLE TRIGGER ALL出现类似错误permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger(我在Amazon RDS上收到此错误),请尝试以下操作:

set session_replication_role to replica;
Run Code Online (Sandbox Code Playgroud)

如果成功,则将禁用所有基于表约束的触发器。现在由您来确保您所做的更改使数据库保持一致状态!

然后,完成后,使用以下命令重新启用会话的触发器和约束:

set session_replication_role to default;
Run Code Online (Sandbox Code Playgroud)

  • 与 Postgres 12 配合良好。请注意,您需要成为超级用户才能设置 session_replication_role 。 (2认同)