【问题标题】:sql delete with foreign key constraint带有外键约束的sql删除
【发布时间】:2014-09-13 04:08:53
【问题描述】:

我有三个表,其中一个是关系表,例如:

People 有主键:peopleid;
Pet 表有主键:petid;
Own表有两个外键:peopleidpetid,它们共同作为Own表的主键

现在
我正在尝试从给定 peopleid 的 Table People 中删除一个人,同时应该删除此人拥有的宠物,以及存储在 table Own 中的关系

请多多指教,先谢谢了!

编辑: 我的问题是如何编写 sql 来实现这一点。

【问题讨论】:

  • 阐明什么?有什么问题?
  • @LarryLustig 我正在尝试编写 sql 来实现这一点..

标签: mysql sql


【解决方案1】:

我想你需要的是ON DELETE CASCADE
使用它,如果您删除主键行,您可以自动删除所有表引用。

在您的情况下,如果您使用peopleid 删除一个人,它会自动从Own 表中删除引用。

示例 SQL 语句

CREATE TABLE Own (
peopleid int(11) NOT NULL,
KEY peopleid (peopleid),
FOREIGN KEY (peopleid)
REFERENCES People (peopleid)
ON DELETE CASCADE
) ENGINE=InnoDB;  

由于宠物表没有人表的外键,因此您需要定义一个触发器以在删除人员条目时自动删除宠物条目。

CREATE TRIGGER pet_delete AFTER DELETE on own
FOR EACH ROW
BEGIN
    DELETE FROM Pet
    WHERE Pet.petid = old.petid;
END  

定义级联规则和触发器后,如果执行:

DELETE FROM People WHERE peopleid = 3  

它将自动从own 表中删除带有peopleid = 3 的条目,并从Pet 表中删除相应的petid

查看此链接了解更多详情。
http://www.mysqltutorial.org/mysql-on-delete-cascade/
MySQL Trigger: Delete From Table AFTER DELETE

【讨论】:

  • people 表中的行被删除时,ON DELETE CASCADE 规则适用于从own 表中删除关联的行,但不适用于pet 表。 (因为外键来自引用pet 表的own 表。)
  • 是的,你是对的。对于宠物表,需要定义一些触发器
  • 正如@spencer7593 在他的回答中提到的那样,一只宠物可能由多个人拥有。这种情况下,我不想删除表 Pet....中的宠物。我应该如何修改触发器来实现这一点?
  • 哦,我可以这样做:从 Pet WHERE Pet.petid = old.petid AND NOT EXISTS(SELECT * FROM Pet WHERE petid = old.petid) 中删除;
  • 我觉得应该是... SELECT * FROM Own WHERE petid = old.petid
【解决方案2】:

一种选择是使用多表 DELETE 语句,从所有三个表中删除行,例如:

DELETE o.*
     , t.*
     , p.*
  FROM pet t
  JOIN own o
    ON o.petid = t.petid
  JOIN people p
    ON p.peopleid = o.peopleid
 WHERE p.peopleid = :b_peopleid

但是...外键存在问题。可以按照导致违反外键约束的顺序删除行。这不是 MyISAM 的问题,因为它不强制执行外键。但是对于 InnoDB,如果定义了外键约束,这可能是一个问题不幸的是,InnoDB 不(还没有?)支持可延迟约束,因此最接近的解决方法是暂时 disable 外键检查...

SET foreign_key_checks = 0 ;

然后DELETE语句,然后重新启用外键检查...

SET foreign_key_checks = 1; 

但这会禁用会话中的所有外键检查,而不仅仅是在 DELETE 中引用的表上定义的外键。因此,这种方法不太理想,因为可能会引入不一致的数据。

(例如,如果另一个表的外键引用了 pet.petid,并且我们从 pet 中删除了“父”行,则可能会在“子”表中留下引用不存在键的行.)

pet删除行?

数据模型表明pet 可能与多个people 相关。例如:

宠物:petid petname ------ ------ 1 点 人:人名 -------- ------ 2 杰克 3 吉尔 拥有:petid peopleid -------- -------- 1 2 1 3

如果我们要删除“Jack”,我们可以删除“own”中的关联行(“Jack”和“Spot”之间的关系。但在这种情况下,“Spot”也与“Jill”相关。 own 中的另一行引用了“Spot”,因此我们无法删除“Spot”,同时也删除了“Spot”与“Jill”的关系。

那么,问题是,在这种情况下,我们真的要删除“Spot”吗?

如果我们真的想删除“Spot”,我们还需要从own 中删除另一行,即与“Spot”和“Jill”相关的那一行。我们可以使用对own 表的两个 引用来完成此操作;一个获取“Jack”与他拥有的pet 之间的关系,另一个获取所有引用“Spot”的own 行。

DELETE r.*
     , t.*
     , p.*
  FROM pet t
  JOIN own r
    ON r.petid = t.petid
  JOIN own o
    ON o.petid = t.petid
  JOIN people p
    ON p.peopleid = o.peopleid
 WHERE p.peopleid = :b_peopleid

另一方面,如果我们不想删除“Spot”,因为own 中的“Jill”/“Spot”行也引用了“Spot”,我们也许可以做一些非常相似的事情。 (我没有这方面的 SQL 示例。)


处理外键关系的其他一些可能适用于您的用例的选项是 BEFORE DELETE 触发器和/或外键约束的 ON DELETE 规则。)

如果我们在引用 peoplepetown 的外键上定义了 ON DELETE CASCADE 规则,我们可以允许它从 own 中删除行。

然后我们可以在我的回答中使用第一个示例 DELETE 语句,我们可以在 DELETE 列表中省略对o.* 的引用,而只指定t.*p.*。 (我们仍然需要在 FROM 子句中引用own 表,以获取people(“Jack”)和pet(“Spot”)之间的关系,因此我们知道从pet 到哪些行删除。


否则,您可以运行三个单独的 DELETE 语句。但是执行 DELETE 语句的顺序实际上可能会删除您需要的信息,以确定需要删除其他表中的哪些行。这意味着您可能需要查询表以查找要删除的行,保存该信息,然后按适当的顺序执行所需的删除。


解决这种“删除”问题的另一种可行方法是通过更新行上的“deleted_flag”类型列来模拟删除。

也就是说,应用程序不会发出 DELETE 语句,而是发出 UPDATE 以在每一行上设置一个特殊用途的“deleted_flag”。但是,必须为此设计应用程序用例,并且几乎所有查找“未删除”数据的查询都需要合并排除“已删除”行的谓词。如果逻辑上删除的行确实确实需要从表中删除,那么 DELETE 语句可以由单独的批处理运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-14
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多