在 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
(每次)是剩下的选项。
【讨论】:
我们没有创建带有 ONDELETE 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 中使用外键删除行的主要内容,如果未能解决你的问题,请参考以下文章