简体   繁体   English

带有外键约束的sql删除

[英]sql delete with foreign key constraint

I have three tables, one of them is a relation table, for example: 我有三个表,其中一个是关系表,例如:

Table People has primary key : peopleid ; 具有主键:人id ;
Table Pet has primary key : petid ; 宠物有主键: 皮蒂 ;
Table Own has two foreign keys: peopleid and petid , they together serve as the primary key of table Own Table Own有两个外键: peopleidpetid ,它们一起用作Table Own的主键

Now 现在
I'm trying to delete a person from Table People given peopleid , meanwhile, the pets this person own should be deleted, as well as the relationship stored in table Own 我正在尝试从给定peopleid的 Table People中删除一个人,与此同时,应该删除该人拥有的宠物以及存储在Own表中的关系

Please shed some light, thanks in advance! 请说明一下,预先感谢!

Edit: My question is how to write the sql to achieve this. 编辑:我的问题是如何编写sql以实现此目的。

One option is to use a multitable DELETE statement, to delete rows from all three tables, for example: 一种选择是使用多表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

But... there's an issue with foreign keys. 但是...外键存在问题。 The rows may be deleted in an order that causes foreign key constraints to be violated. 可以按导致违反外键约束的顺序删除行。 This isn't a problem with MyISAM, since it doesn't enforce foreign keys. MyISAM没问题,因为它不强制使用外键。 But with InnoDB, if there are foreign key constraints defined, this can be a problem Unfortunately, InnoDB doesn't (yet?) support deferrable constraints, so the closest workaround to this is to temporarily disable foreign key checks... 但是使用InnoDB时,如果定义了外键约束,这可能是个问题。不幸的是,InnoDB不(还?)支持可延期约束,因此最接近的解决方法是暂时禁用外键检查...

SET foreign_key_checks = 0 ;

then the DELETE statement, and then re-enable the foreign key checks... 然后是DELETE语句,然后重新启用外键检查...

SET foreign_key_checks = 1; 

But this disables ALL foreign key checks in the session, not just the foreign keys defined on the tables referenced in the DELETE. 但这会禁用会话中的所有外键检查,而不仅是在DELETE中引用的表上定义的外键。 So, this approach is less than ideal, because of the potential for introducing inconsistent data. 因此,这种方法不太理想,因为可能会引入不一致的数据。

(For example, if there's another table with a foreign key that references pet.petid, and we delete the "parent" row from pet, that may leave a rows in the "child" table that reference a non-existent key.) (例如,如果存在另一个带有引用pet.petid的外键的表,并且我们从pet中删除了“父”行,则可能会在“子”表中留下引用不存在的键的行。)

DELETE row(s) from pet ? pet删除行?

The data model suggests that a pet may be related to more than one people . 数据模型表明, pet可能与一个以上的people For example: 例如:

pet:    petid    petname
            ------   -------
            1        Spot

    people: peopleid name
            -------- ------
            2        Jack
            3        Jill

    own:    petid    peopleid
            -------  --------
            1        2
            1        3

If we were to delete "Jack", we could remove the associated row in "own" (the relationship between "Jack" and "Spot". But in this scenario, "Spot" is also related to "Jill". There's another row in own that references "Spot", so we wouldn't be able to remove "Spot", without also removing "Spot"'s relationship to "Jill". 如果要删除“ Jack”,则可以删除“ own”中的关联行(“ Jack”和“ Spot”之间的关系。但是在这种情况下,“ Spot”也与“ Jill”相关。还有另一行own引用了“ Spot”,因此如果不删除“ Spot”与“ Jill”的关系,我们将无法删除“ Spot”。

So, the question is, do we really want to delete "Spot" in this case? 因此,问题是,在这种情况下,我们真的要删除“ Spot”吗?

If we do really want to delete "Spot", we would also need to remove that other row from own too, the one that relates "Spot" and "Jill". 如果确实要删除“ Spot”,则也需要从own那一行中删除另一行,即与“ Spot”和“ Jill”相关的那一行。 We can accomplish that using two references to the own table; 我们可以使用对own表的两个引用来完成此操作。 one to get the relationship between "Jack" and his owned pet , and another to get all of the own rows that reference "Spot". 一个用于获取“ 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

On the other hand, if we don't want to remove "Spot", because the "Jill"/"Spot" row in own also references "Spot", we might be able to do something very similar. 在另一方面,如果我们不希望删除“点”,因为“吉尔” /“点”行own也引用“点”,我们也许能够做一些非常相似。 (I don't a SQL example for this.) (我没有为此的SQL示例。)


Some other options for dealing with foreign key relationships that may be workable for your use-case are BEFORE DELETE triggers, and/or ON DELETE rules on the foreign key constraints.) 在您的用例中可能有用的处理外键关系的其他一些选项是DELETE触发器之前,和/或关于外键约束的ON DELETE规则。)

If we had ON DELETE CASCADE rules defined on the foreign keys from own that reference people and pet , we could allow that to remove the rows from own . 如果我们对删除的外键从已定义CASCADE规则, own是参照peoplepet ,我们可以允许从删除的行own

Then we could use that first example DELETE statement in my answer, and we could omit the reference to o.* in the DELETE list, and just specify t.* and p.* . 然后,我们可以在答案中使用第一个示例DELETE语句,并且可以省略DELETE列表中对o.*的引用,而只需指定t.*p.* (We still need the reference to the own table in the FROM clause, to get the relationship between people ("Jack") and pet ("Spot"), so we know which rows from pet to remove. (我们仍然需要在FROM子句中引用own表,以获取people (“杰克”)和pet (“ Spot”)之间的关系,因此我们知道要删除pet哪些行。


Otherwise, you could run three separate DELETE statements. 否则,您可以运行三个单独的DELETE语句。 But the order that the DELETE statements are executed may actually remove information you need, to determine which rows in the other tables need to be deleted. 但是执行DELETE语句的顺序实际上可能会删除您需要的信息,以确定需要删除其他表中的哪些行。 This means you would likely need to query the tables to find the rows to be deleted, save that information, and then perform the required deletes, in an appropriate order. 这意味着您可能需要查询表以找到要删除的行,保存该信息,然后以适当的顺序执行所需的删除。


Another workable approach to this kind of "delete" problem is to emulate a delete with an update of a "deleted_flag" type column on the row. 解决此类“删除”问题的另一种可行方法是模拟删除,并在行上更新“ deleted_flag”类型列。

That is, rather than issue a DELETE statement, the application instead issues an UPDATE to set a special purpose "deleted_flag" on each row. 也就是说,应用程序不是发出DELETE语句,而是发出UPDATE以在每一行上设置特殊用途的“ deleted_flag”。 But, the application use cases have to be designed for this, and almost all the queries that look for "non-deleted" data need to incorporate predicates that exclude the "deleted" rows. 但是,必须为此设计应用程序用例,并且几乎所有查找“未删除”数据的查询都需要包含排除“已删除”行的谓词。 If logically deleted rows do actually do need to be deleted from the tables, then the DELETE statements can be run by a separate batch process. 如果实际上确实需要从表中删除逻辑删除的行,则可以通过单独的批处理过程运行DELETE语句。

I think what you need is ON DELETE CASCADE . 我认为您需要的是ON DELETE CASCADE
Using this, you can automatically remove all the table references, if you delete a primary key row. 使用此方法,如果删除主键行,则可以自动删除所有表引用。

In your case, if you delete a person using peopleid , it will automatically remove the references from Own table. 在你的情况,如果你删除使用一个人peopleid ,它会自动删除引用Own表。

Sample SQL statement 示例SQL语句

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

Since pet table has no foreign key to people table, you need to define a trigger to auto delete pet entries when people entries are deleted. 由于宠物表没有到人员表的外键,因此您需要定义一个触发器,以便在删除人员条目时自动删除宠物条目。

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

After defining the cascade rule and trigger, if you execute: 定义级联规则和触发器后,如果执行:

DELETE FROM People WHERE peopleid = 3  

it will automatically delete entries from own table with peopleid = 3 and the corresponding petid from Pet table. 它会自动删除条目ownpeopleid = 3和相应的petidPet表。

Check this link for more details. 检查此链接以获取更多详细信息。
http://www.mysqltutorial.org/mysql-on-delete-cascade/ http://www.mysqltutorial.org/mysql-on-delete-cascade/
MySQL Trigger: Delete From Table AFTER DELETE MySQL触发器:删除后从表中删除

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM