【问题标题】:When to use "ON UPDATE CASCADE"何时使用“ON UPDATE CASCADE”
【发布时间】:2010-12-01 16:12:15
【问题描述】:

我经常使用ON DELETE CASCADE,但我从不使用ON UPDATE CASCADE,因为我不确定它在什么情况下会有用。

为了便于讨论,让我们看一些代码。

CREATE TABLE parent (
    id INT NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (id)
);

CREATE TABLE child (
    id INT NOT NULL AUTO_INCREMENT, parent_id INT,
    INDEX par_ind (parent_id),
    FOREIGN KEY (parent_id)
        REFERENCES parent(id)
        ON DELETE CASCADE
);

对于ON DELETE CASCADE,如果删除带有id的父级,则将自动删除带有parent_id = parent.id的子级记录。这应该没问题。

  1. 这意味着当父级的id 更新时ON UPDATE CASCADE 会做同样的事情?

  2. 如果 (1) 为真,则表示如果 parent.id 不可更新(或永远不会更新),则无需使用 ON UPDATE CASCADE,例如 AUTO_INCREMENT 或始终设置为 @987654333 @。对吗?

  3. 如果 (2) 不成立,在什么情况下我们应该使用ON UPDATE CASCADE

  4. 如果我(出于某种原因)将child.parent_id 更新为不存在的东西,它会被自动删除吗?

嗯,我知道,上面的一些问题可以通过编程测试来理解,但我也想知道这些问题是否依赖于数据库供应商。

请多指教。

【问题讨论】:

标签: sql foreign-keys


【解决方案1】:

确实,如果您的主键只是一个自动递增的标识值,那么 ON UPDATE CASCADE 将没有真正的用途。

但是,假设您的主键是 10 位 UPC 条形码,由于扩展,您需要将其更改为 13 位 UPC 条形码。在这种情况下,ON UPDATE CASCADE 将允许您更改主键值,并且任何具有对该值的外键引用的表都将相应更改。

参考 #4,如果您将子 ID 更改为父表中不存在的内容(并且您具有引用完整性),您应该会收到外键错误。

【讨论】:

  • 只需要自己使用ON UPDATE CASCADE 来更新不使用自动递增键的旧表中的主键
【解决方案2】:
  1. 是的,这意味着例如,如果您执行UPDATE parent SET id = 20 WHERE id = 10,则所有 10 的孩子 parent_id 也将更新为 20

  2. 如果不更新外键引用的字段,则不需要此设置

  3. 想不出其他用途。

  4. 您不能这样做,因为外键约束会失败。

【讨论】:

    【解决方案3】:

    我认为你已经抓住了要点!

    如果您遵循数据库设计最佳实践并且您的主键永远不可更新(我认为无论如何都应该如此),那么您永远不需要ON UPDATE CASCADE 子句。

    Zed 提出了一个很好的观点,如果您使用 自然 键(例如数据库表中的常规字段)作为主键,那么可能有需要更新主键的情况。另一个最近的例子是 ISBN(国际标准书号)不久前从 10 位变为 13 位数字+字符。

    如果您选择使用代理(例如人为系统生成的)键作为主键(除了极少数情况外,这将是我的首选),情况并非如此。

    最后:如果你的主键永远不会改变,那么你永远不需要ON UPDATE CASCADE 子句。

    马克

    【讨论】:

    • 什么是“人工系统生成的”密钥? UUID?
    • @HPWD:只是一个由系统生成的“人工”密钥(一个不基于或衍生自您的实际数据的值) - 它可以是 GUID,或 INT,或 BIGINT - 或任何真正的东西 - 都无关紧要。重点是:该值与您自己的实际数据没有任何关系——系统会自动为您生成该值。
    • @marc-s 感谢您花时间写出来。你的回答很有道理。
    • 在我看来,具有自然键的单列表是枚举的一个很好且干净的替代方案(至少在 MySQL DB 风格中)。例如,考虑包含行 bluepurpleyellow 的表 colors,以及带有 product_color 列的表 products,它们被 FK'ed 到 colors 表。这限制了枚举之类的选择,但与自动递增整数不同,它不需要连接来获取颜色名称。在这种情况下,on update cascade 是个好主意,如果您决定将 purple 改为 violet
    【解决方案4】:

    几天前,我遇到了触发器问题,我发现ON UPDATE CASCADE 很有用。看看这个例子(PostgreSQL):

    CREATE TABLE club
    (
        key SERIAL PRIMARY KEY,
        name TEXT UNIQUE
    );
    
    CREATE TABLE band
    (
        key SERIAL PRIMARY KEY,
        name TEXT UNIQUE
    );
    
    CREATE TABLE concert
    (
        key SERIAL PRIMARY KEY,
        club_name TEXT REFERENCES club(name) ON UPDATE CASCADE,
        band_name TEXT REFERENCES band(name) ON UPDATE CASCADE,
        concert_date DATE
    );
    

    在我的问题中,我必须定义一些额外的操作(触发器)来更新 Concert 的表格。这些操作必须修改 club_name 和 band_name。由于参考,我无法做到这一点。我不能修改音乐会,然后处理俱乐部和乐队的桌子。我也不能这样做。 ON UPDATE CASCADE 是解决问题的关键。

    【讨论】:

    • 好评。我发现更新级联也很有用,无论如何您都必须更改您的 ID。我也同意其他人的观点,即这种变化不应该如此典型。例如,在您引用的情况下,我认为在大量数据中,使用文本字段关联外键可能不会导致数据库引擎的最快性能。请注意,如果 Concert 表中的外部关系使用 club.SERIAL 和 band.SERIAL,则名称的更改不会影响表之间的关系。但是,我发现 ON UPDATE CASCADE 是解决紧急情况的好工具。问候
    • 这是一个有问题的设计,但它是一个相当人为的例子。如果您引用names 而不是每个表的主键,那么在clubband 中保留两个SERIAL 列作为主键有什么意义?
    • 简而言之,如果您从其他表中复制字段并且您需要它是最新的,这很有用。
    【解决方案5】:

    我的评论主要参考第 3 点:如果我们假设父键不可更新,在什么情况下 ON UPDATE CASCADE 适用?这是一个案例。

    我正在处理一个复制场景,其中多个卫星数据库需要与一个主数据库合并。每个卫星都在相同的表上生成数据,因此将表合并到主表会导致违反唯一性约束。我正在尝试使用 ON UPDATE CASCADE 作为解决方案的一部分,在该解决方案中,我在每次合并期间重新增加密钥。 ON UPDATE CASCADE 应该通过自动化部分过程来简化这个过程。

    【讨论】:

      【解决方案6】:

      ON UPDATEON DELETE 指定在更新和删除父表中的行时将执行哪个操作。以下是允许的操作:NO ACTIONCASCADESET NULLSET DEFAULT

      删除父表中行的动作

      如果您删除父表中的一行或多行,您可以设置以下操作之一:

      • ON DELETE NO ACTION:SQL Server 引发错误并回滚对父表中行的删除操作。
      • ON DELETE CASCADE:SQL Server 删除子表中与父表中删除的行对应的行。
      • ON DELETE SET NULL:如果父表中的相应行被删除,SQL Server 会将子表中的行设置为 NULL。要执行此操作,外键列必须可以为空。
      • ON DELETE SET DEFAULT:如果父表中的相应行被删除,SQL Server 会将子表中的行设置为其默认值。要执行此操作,外键列必须具有默认定义。请注意,如果未指定默认值,可空列的默认值为 NULL。 默认情况下,如果您没有明确指定任何操作,SQL Server 将应用 ON DELETE NO ACTION。

      父表中行的更新操作

      如果您更新父表中的一行或多行,您可以设置以下操作之一:

      • ON UPDATE NO ACTION:SQL Server 引发错误并回滚父表中行的更新操作。
      • ON UPDATE CASCADE:当父表中的行更新时,SQL Server 会更新子表中的相应行。
      • ON UPDATE SET NULL:当父表中的相应行更新时,SQL Server 将子表中的行设置为 NULL。请注意,要执行此操作,外键列必须可以为空。
      • ON UPDATE SET DEFAULT:SQL Server 为子表中更新了父表中相应行的行设置默认值。
      FOREIGN KEY (foreign_key_columns)
          REFERENCES parent_table(parent_key_columns)
          ON UPDATE <action> 
          ON DELETE <action>;
      

      the reference tutorial

      【讨论】:

        【解决方案7】:

        这是一个很好的问题,我昨天也有同样的问题。我考虑过这个问题,特别是搜索是否存在类似“ON UPDATE CASCADE”之类的东西,幸运的是 SQL 的设计者也考虑到了这一点。我同意 Ted.strauss,我也评论了 Noran 的案例。

        我什么时候用的?就像 Ted 指出的那样,当您一次处理多个数据库时,其中一个数据库中的修改,在一张表中,在 Ted 所谓的“卫星数据库”中具有任何形式的复制,不能与原始数据库保持一致ID,并且出于任何原因您必须创建一个新的,以防您无法更新旧的数据(例如由于权限,或者如果您在一个如此短暂的情况下搜索快速性不值得对规范化的全部规则绝对和完全尊重,仅仅是因为这将是一个非常短暂的实用程序)

        所以,我同意两点:

        (A.) 是的,很多时候更好的设计可以避免它;但是

        (B.) 在迁移、复制数据库或解决紧急情况的情况下,这是一个很棒的工具,幸运的是,当我去搜索它是否存在时,它就在那里。

        【讨论】:

          猜你喜欢
          • 2015-01-27
          • 1970-01-01
          • 1970-01-01
          • 2014-06-28
          • 1970-01-01
          • 2014-10-15
          • 1970-01-01
          • 1970-01-01
          • 2012-08-07
          相关资源
          最近更新 更多