在 PostgreSQL 中使用外键删除行

Posted

技术标签:

【中文标题】在 PostgreSQL 中使用外键删除行【英文标题】:Delete rows with foreign key in PostgreSQL 【发布时间】:2012-12-20 08:43:31 【问题描述】:

我想删除包含外键的行,但是当我尝试这样的事情时:

DELETE FROM osoby WHERE id_osoby='1'

我明白了:

错误:更新或删除表“osoby”违反了表“kontakty”上的外键约束“kontakty_ibfk_1” 详细信息:键 (id_osoby)=(1) 仍从表“kontakty”中引用。

如何删除这些行?

【问题讨论】:

也检查一下on delete cascade ;) 不过,最好将这些设置放在您的表格中。当creating foreign keys 我们add parent then child 时。所以当删除我们delete child and then parent ;) 【参考方案1】:

要自动执行此操作,您可以使用 ON DELETE CASCADE 定义外键约束。 我引用the manual for foreign key constraints:

CASCADE 指定当引用的行被删除时,行 引用它也应该被自动删除。

像这样查找当前的 FK 定义:

SELECT pg_get_constraintdef(oid) AS constraint_def
FROM   pg_constraint
WHERE  conrelid = 'public.kontakty'::regclass  -- assuming public schema
AND    conname = 'kontakty_ibfk_1';

然后在如下语句中将ON DELETE ... 部分添加或修改为ON DELETE CASCADE(保留其他所有内容):

ALTER TABLE kontakty
   DROP CONSTRAINT kontakty_ibfk_1
 , ADD  CONSTRAINT kontakty_ibfk_1
   FOREIGN KEY (id_osoby) REFERENCES osoby (id_osoby) ON DELETE CASCADE;

没有ALTER CONSTRAINT 命令。在单个 ALTER TABLE 语句中删除并重新创建约束,以避免并发写入访问可能出现的竞争条件。

显然,您需要特权才能这样做。该操作在表kontakty 上使用ACCESS EXCLUSIVE 锁定,在表osoby 上使用SHARE ROW EXCLUSIVE 锁定。

如果您不能ALTER 表,那么手动删除(一次)或触发BEFORE DELETE(每次)是剩下的选项。

【讨论】:

我们没有创建带有 ON DELETE CASCADE 的表。我不能改变表的结构。有没有办法自动删除外键?或者我们必须遵循@juergen d 建议的方式 @Varun: 如果你不能ALTER 表添加带有ON DELETE CASCADE 的FK,那么手动删除(一次)或触发BEFORE DELETE(每次)是剩余的选项。 很酷,所以答案是将ON DELETE CASCADE 添加到您创建语句中带有外键的列中。 @425nesp:我添加了明确的说明。 在我的情况下,我需要 DDL 来查看确切的名称约束,在这种情况下它将是 \d kontakty【参考方案2】:

不建议将此作为通用解决方案,但对于一次性删除数据库中未生产或未使用的行,您可以暂时禁用相关表上的触发器。

就我而言,我处于开发模式,并且有几个表通过外键相互引用。因此,删除它们的内容并不像先从一个表中删除所有行那么简单。所以,对我来说,删除它们的内容效果很好,如下所示:

ALTER TABLE table1 DISABLE TRIGGER ALL;
ALTER TABLE table2 DISABLE TRIGGER ALL;
DELETE FROM table1;
DELETE FROM table2;
ALTER TABLE table1 ENABLE TRIGGER ALL;
ALTER TABLE table2 ENABLE TRIGGER ALL;

您应该能够根据需要添加 WHERE 子句,当然要小心避免破坏数据库的完整性。

http://www.openscope.net/2012/08/23/subverting-foreign-key-constraints-in-postgres-or-mysql/ 有一些很好的相关讨论

【讨论】:

死链接的存档版本:web.archive.org/web/20160922175428/http://www.openscope.net/…。 (建议的编辑队列已满,所以我没有足够的代表自己编辑) 当需要从一张巨大的桌子上剪下一部分进行测试(比如 1 年)时会有很大帮助!【参考方案3】:

如果外键仍然引用另一个表,则不能删除它。 先删除引用

delete from kontakty
where id_osoby = 1;

DELETE FROM osoby 
WHERE id_osoby = 1;

【讨论】:

【参考方案4】:

问这个问题已经有一段时间了,希望能帮到你。 因为您无法更改或更改 db 结构,所以您可以这样做。根据 postgresql docs.

TRUNCATE -- 清空一个表或一组表。

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

说明

TRUNCATE 快速从一组表中删除所有行。它与每个表上的非限定 DELETE 具有相同的效果,但由于它实际上并不扫描表,因此速度更快。此外,它会立即回收磁盘空间,而不需要后续的 VACUUM 操作。这在大表上最有用。


截断表 othertable,并级联到通过外键约束引用 othertable 的任何表:

TRUNCATE othertable CASCADE;

同样,也重置任何关联的序列生成器:

TRUNCATE bigtable, fattable RESTART IDENTITY;

截断并重置任何关联的序列生成器:

TRUNCATE revinfo RESTART IDENTITY CASCADE ;

【讨论】:

不错,很有用。 有史以来最好的答案 :)【参考方案5】:

这意味着在表 kontakty 中有一行引用了要删除的 osoby 中的行。您必须先删除该行或对表之间的关系设置级联删除。

Powodzenia!

【讨论】:

以上是关于在 PostgreSQL 中使用外键删除行的主要内容,如果未能解决你的问题,请参考以下文章

在 PostgreSQL Flask 中删除具有外键的行

postgreSQL的主外键

完整性错误:更新或删除违反外键约束。 Django + PostgreSQL

PostgreSQL 行级安全性涉及与其他表的外键

删除表时的外键依赖项

尝试在 Django 中使用外键列表创建 PostgreSQL 字段