【问题标题】:Foreign key constraints: When to use ON UPDATE and ON DELETE外键约束:何时使用 ON UPDATE 和 ON DELETE
【发布时间】:2011-10-06 21:31:03
【问题描述】:

我正在使用 MySQL Workbench 设计我的数据库架构,这非常酷,因为您可以制作图表并转换它们:P

无论如何,我决定使用 InnoDB 是因为它支持外键。我注意到的一件事是它允许您为外键设置 On Update 和 on Delete 选项。谁能解释一下“Restrict”、“Cascade”和 set null 可以在一个简单的例子中使用吗?

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

谢谢

【问题讨论】:

    标签: mysql sql database foreign-keys


    【解决方案1】:

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

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

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

    就我个人而言,我会使用 InnoDB,因为它不会破坏您的数据(参见 MyISAM,它确实如此),而不是因为它具有 FK 约束。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 这将是不使用该特定 ORM 的理由。任何在数据库支持方面表现不佳的工具都不值得信赖。外键和级联删除或更新是数据库基础而不是高级概念,任何现实数据库都不应该在没有外键约束的情况下设计!
      • 问题是他们抛出错误。是否可以限制删除但引擎不会产生错误但仍保持语义?我希望我的程序继续运行,同时保护其他数据不被删除。
      【解决方案3】:

      毫不犹豫地对数据库施加约束。您一定会拥有一个一致的数据库,这是使用数据库的充分理由之一。特别是如果您有多个应用程序请求它(或只有一个应用程序,但具有直接模式和使用不同来源的批处理模式)。

      使用 MySQL,您不会像在 postgreSQL 中那样拥有高级约束,但至少外键约束非常高级。

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

      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,如果一个用户至少链接到该公司,引擎将拒绝该操作。
      • 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。这很危险,但可用于对辅助表进行自动清理(因此它可以是您想要的,但对于 COMPANYUSER 示例肯定不是)
      • ON DELETE SET NULL : handful : 如果您删除 COMPANY 行,相关的 USER 将自动与 NULL 建立关系。如果 Null 是您对没有公司的用户的价值,这可能是一种很好的行为,例如,您可能需要将用户保留在您的应用程序中,作为某些内容的作者,但删除公司对您来说不是问题。

      通常我的默认设置是:ON DELETE RESTRICT ON UPDATE CASCADE。有一些ON DELETE CASCADE 用于跟踪表(日志——不是所有日志——,类似的东西)和ON DELETE SET NULL,当主表是包含外键的表的“简单属性”时,比如 JOB 表USER 表。

      编辑

      很久没写了。现在我想我应该添加一个重要的警告。 MySQL 对级联有一个很大的记录限制。 级联不会触发触发器。因此,如果您对该引擎有足够的信心使用触发器,则应避免级联约束。

      MySQL 触发器仅针对 SQL 语句对表所做的更改激活。它们不会因视图的更改而激活,也不会因不将 SQL 语句传输到 MySQL 服务器的 API 对表的更改而激活

      ==> 见下方最后的编辑,这个域正在发生变化

      外键操作不会激活触发器。

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

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

      12/2017-更新此关于 MySQL 的编辑:

      正如@IstiaqueAhmed 在 cmets 中所说,这个问题的情况已经发生了变化。因此,请点击链接并查看真实的最新情况(将来可能会再次更改)。

      【讨论】:

      • ON DELETE CASCADE : dangerous -- 加少许盐。
      • 您需要小心级联,如果需要更改大量记录,它可能会锁定您的系统。 Cascde 删除在使用前应特别仔细查看,如果有子记录,您通常确实希望不发生删除。我不希望客户删除以清除他之前拥有的订单的财务数据。有时最好确保没有开启级联,并提供一种将记录标记为非活动状态的方法。
      • 在业务逻辑方面,SET NULLON UPDATE 中可能会感兴趣的一种情况:更新公司代表公司>用户关系的分离。例如:如果一家公司更改其业务类型,以前的用户可能不再与该业务相关,因此NULL 可能更适合该索引。
      • @regilero,您第一个指向 mysql 站点的链接 (dev.mysql.com/doc/refman/5.6/en/triggers.html) 中的内容似乎已更改。它说 This includes changes to base tables that underlie updatable views 而不是您粘贴的内容,即 They do not activate for changes in views
      • “我不希望客户删除以清除他之前订单的财务数据。” 在这种情况下,您可能仍然需要客户的反正数据。您的设计可能应该将客户标记为非活动状态,而不是从数据库中删除他们的行。在实践中,根据我的专业经验,您实际上非常很少想要删除任何内容,宁愿默认标记为非活动。在永久删除 可以的情况下,CASCADE DELETE 通常也可以,甚至是首选。我不认为它特别危险。
      猜你喜欢
      • 2013-04-23
      • 2015-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-06
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多