在自引用表中排序删除记录

时间:2014-06-06 05:27:27

标签: sql postgresql

我需要从自引用表中删除记录子集。子集将始终是自包含的(即,记录将仅对要删除的子集中的其他记录的引用,而不是对语句完成时仍将存在的任何记录的引用。)

我的理解是,如果其中一条记录在删除记录之前被删除, 可能 会导致错误。

第一个问题: postgres一次只记录一次这个操作,还是作为一个整体交易?也许我不必担心这个问题?

第二个问题:是删除记录的顺序是一致的还是可预测的?

我显然能够编写特定的SQL来删除这些记录而没有任何错误,但我的最终目标是编写一个回归测试来向我展示下一个为什么我这样编写它的人。我想以这样的方式设置测试数据:由于引用同一个表的记录,简单的删除语句将始终失败。这样,如果其他人稍后会对SQL进行混淆,那么他们会通过测试套件得到通知,我出于某种原因这样做了。

任何人都有见解?

编辑:只是为了澄清,我并没有试图找出如何安全地删除记录(这很简单)。我试图弄清楚哪种情况会导致这样的DELETE语句一直失败。

编辑2 :未来读者的缩写答案:这不是问题。默认情况下,postgres会检查每个 语句 末尾的约束(不是每个记录,而不是每个事务)。在此处的文档中确认:http://www.postgresql.org/docs/current/static/sql-set-constraints.html并在此处通过SQLFiddle:http://sqlfiddle.com/#!15/11b8d/1

4 个答案:

答案 0 :(得分:4)

在标准SQL中,我相信PostgreSQL遵循这一点,每个语句都应该被处理"好像"所有变化同时发生,并行。

以下代码有效:

CREATE TABLE T (ID1 int not null primary key,ID2 int not null references T(ID1));
INSERT INTO T(ID1,ID2) VALUES (1,2),(2,1),(3,3);
DELETE FROM T WHERE ID2 in (1,2);

我们在INSERTDELETE都涉及循环引用,但它的工作正常。

fiddle

答案 1 :(得分:3)

DELETE子句匹配一组记录的单个WHERE将按实现定义的顺序删除这些记录。此订单可能会根据查询计划员的决策,统计信息等进行更改。不会进行排序保证。就像没有SELECT的{​​{1}}一样。 ORDER BY如果没有包含在显式事务中,则在其自己的事务中执行,因此它将作为一个单元成功或失败。

要在PostgreSQL中强制删除顺序,您必须为每条记录执行一次DELETE。您可以将它们包装在显式事务中,以减少执行此操作的开销,并确保它们全部发生或不发生。

PostgreSQL can check foreign keys at three different points

  • 默认值DELETE:在插入/更新/删除行时检查每一行
  • NOT DEFERRABLE:相同,但受DEFERRABLE INITIALLY IMMEDIATE影响而不是在交易结束时检查/ SET CONSTRAINTS DEFERRED
  • SET CONSTRAINTS IMMEDIATE:检查事务
  • 末尾的所有行

在您的情况下,我将您的DEFERRABLE INITIALLY DEFERRED约束定义为FOREIGN KEY,并在删除之前执行DEFERRABLE INITIALLY IMMEDIATE

(实际上,如果我模糊地回忆起来,尽管名称为SET CONSTRAINTS DEFERREDIMMEDIATE实际上会在语句末尾运行检查 而不是每行之后的默认值因此,如果你在一个DEFERRABLE INITIALLY IMMEDIATE中删除整个集合,那么检查就会成功。我需要仔细检查。“

DELETE的温和疯狂含义是由SQL标准定义的IIRC,以及没有时区的DEFERRABLE等宝石。

答案 2 :(得分:0)

1)如果包含在“BEGIN / COMMIT”中,它将作为交易。否则一般没有。

有关详情,请参阅http://www.postgresql.org/docs/current/static/tutorial-transactions.html

一般来说,您的问题的答案取决于如何实现自我引用。 如果它在应用程序逻辑中,那么您自己负责检查这些内容。

否则,通常可以限制或级联具有外键和DELETE CASCADE的行的删除。但是,就PG文档而言,我理解我们正在讨论引用其他表中的列,不确定是否支持同表外键:

http://www.postgresql.org/docs/current/static/ddl-constraints.html#DDL-CONSTRAINTS-FK

2)通常,删除顺序将是您发出删除语句的顺序。如果您希望它们都是“不可中断的”,而中间没有其他语句修改表,则将它们包含在事务中。

作为一个警告,我可能是错的,但你似乎要做的事情,一定不能做。您不应该依赖某些深奥的“删除顺序”或数据库的其他一些未记录和/或隐含的功能。基础逻辑似乎不合适,应该有另一种方式。

答案 3 :(得分:0)

如果您发出影响多条记录的单个DELETE(例如delete from x where id>100),那么它将作为单个事务处理,并且全部将成功或失败。如果有多个DELETE,则必须自己将它们放入事务中。

会有问题。如果您使用DELETE CASCADE有约束,则可能会删除多个DELETE。如果您不这样做,完整性检查可能会阻止您删除。除了NO ACTION之外的约束不可延迟,因此您必须在删除之前禁用约束并在之后启用它(基本上是drop / create,这可能很慢)。

如果您有多个DELETE,那么顺序就像发送DELETE语句一样。如果单个DELETE,数据库将按顺序删除它们(索引,oids,别的......)。

所以我也建议考虑逻辑并且可能以不同的方式处理删除。你能详细说明一下实际的逻辑吗?数据库中的一棵树?