关于mysql:外键约束:何时使用ON UPDATE和ON DELETE

Foreign key constraints: When to use ON UPDATE and ON DELETE

我正在使用MySQL Workbench设计我的数据库模式,这非常酷,因为你可以做图表并转换它们:P

无论如何,我决定使用InnoDB因为它的外键支持。 我注意到的一件事是,它允许您为外键设置On Update和Delete选项。 有人可以解释在一个简单的例子中可以使用"限制","级联"和设置null的位置吗?

例如,假设我有一个包含userIDuser表。 并说我有一个消息表message,它是一个多对多的,有2个外键(引用相同的主键,user表中的userID)。 在这种情况下,设置On Update和On Delete选项是否有用? 如果是这样,我选择哪一个? 如果这不是一个好例子,你能否提出一个很好的例子来说明这些如何有用?

谢谢


不要犹豫,对数据库施加约束。您将确保拥有一致的数据库,这是使用数据库的一个很好的理由。特别是如果您有多个应用程序请求它(或只有一个应用程序,但使用直接模式和使用不同来源的批处理模式)。

使用MySQL,您没有像在postgreSQL中那样的高级约束,但至少外键约束非常先进。

我们举一个例子,一个公司表,其中包含一个包含来自theses公司的人的用户表

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT,
     user_name VARCHAR(50),
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

我们来看看ON UPDATE子句:

  • ON UPDATE RESTRICT:默认值:如果您尝试更新表COMPANY中的company_id,如果一个USER至少链接到该公司,则引擎将拒绝该操作。
  • ON UPDATE NO ACTION:与RESTRICT相同。
  • ON UPDATE CASCADE:通常最好的一个:如果更新表COMPANY中的某一行中的company_id,引擎将在引用此COMPANY的所有USER行上相应地更新它(但在USER表上没有激活触发器,警告)。引擎会跟踪你的变化,这很好。
  • ON UPDATE SET NULL:如果更新表COMPANY行中的company_id,引擎会将相关的USERs company_id设置为NULL(应该在USER company_id字段中可用)。在更新中我看不出任何有趣的事情,但我可能错了。

现在在ON DELETE方面:

  • ON DELETE RESTRICT:默认值:如果您尝试删除表COMPANY中的company_id Id,如果一个USER至少链接到该公司,引擎将拒绝该操作,可以挽救您的生命。
  • ON DELETE NO ACTION:与RESTRICT相同
  • ON DELETE CASCADE:危险:如果删除表COMPANY中的公司行,引擎将删除相关的USER。这很危险,但可用于在辅助表上进行自动清理(因此它可能是您想要的,但肯定不适用于COMPANY < - > USER示例)
  • ON DELETE SET NULL:少数:如果删除COMPANY行,相关的USER将自动将关系设置为NULL。如果Null是您没有公司的用户的价值,这可能是一个很好的行为,例如,您可能需要将用户保留在您的应用程序中,作为某些内容的作者,但删除公司对您来说不是问题。

通常我的默认值是:ON DELETE RESTRICT ON UPDATE CASCADE。当主表是包含外键的表的"简单属性"时,一些ON DELETE CASCADE用于轨道表(日志 - 不是所有日志 - ,类似的东西)和ON DELETE SET NULL,就像一个JOB表用于USER表。

编辑

我写这篇文章已经很久了。现在我想我应该添加一个重要警告。 MySQL对级联有一个很大的记录限制。级联不是触发触发器。因此,如果您对该引擎过于自信以使用触发器,则应避免级联约束。

  • http://dev.mysql.com/doc/refman/5.6/en/triggers.html

MySQL triggers activate only for changes made to tables by SQL statements. They do not activate for changes in views, nor by changes to tables made by APIs that do not transmit SQL statements to the MySQL Server

  • http://dev.mysql.com/doc/refman/5.6/en/stored-program-restrictions.html#stored-routines-trigger-restrictions

==>请参阅下面的最后一次编辑,这个域名正在发生变化

Triggers are not activated by foreign key actions.

而且我认为有一天这个问题不会得到解决。外键约束由InnoDb存储管理,触发器由MySQL SQL引擎管理。两者都是分开的。 Innodb是唯一具有约束管理的存储,也许它们有一天会直接在存储引擎中添加触发器,也许不会。

但我对自己应该在糟糕的触发器实现和非常有用的外键约束支持之间选择哪个元素有自己的看法。一旦你习惯了数据库的一致性,你就会喜欢PostgreSQL。

12/2017 - 更新此编辑MySQL:

正如@IstiaqueAhmed在评论中所说,情况已经发生了变化。因此,请点击链接并检查真实的最新情况(将来可能会再次发生变化)。


除了@MarkR答案之外 - 有一点需要注意的是,许多带有ORM的PHP框架无法识别或使用高级数据库设置(外键,级联删除,唯一约束),这可能会导致意外行为。

例如,如果使用ORM删除记录,并且DELETE CASCADE将删除相关表中的记录,则ORM尝试删除这些相关记录(通常是自动的)将导致错误。


您需要在应用程序的上下文中考虑这一点。通常,您应该设计一个应用程序,而不是数据库(数据库只是应用程序的一部分)。

考虑您的应用程序应如何响应各种情况。

默认操作是限制(即不允许)操作,这通常是您想要的,因为它可以防止愚蠢的编程错误。但是,在DELETE CASCADE上也很有用。这实际上取决于您的应用程序以及您打算如何删除特定对象。

就个人而言,我会使用InnoDB,因为它不会丢弃你的数据(c.f.MyISAM,它确实如此),而不是因为它有FK约束。