一种选择是使用多表 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 规则。)
如果我们在引用 people 和 pet 的 own 的外键上定义了 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 语句可以由单独的批处理运行。