跳至主要内容

SQL ON DELETE CASCADE:自动删除依赖数据

了解 SQL ON DELETE CASCADE 如何自动执行依赖删除、保持表一致,并帮助防止意外数据丢失。
· 9分钟 阅读

使用 AI 探索

在 SQL 中删除数据并不只是移除一行。在关系型数据库中,表通常彼此关联,一个表中的删除可能会影响到其他表。为安全管理这种情况,SQL 使用外键约束来定义当行被删除或更新时,相关数据应如何处理。

最强大的删除选项之一是 SQL 的 ON DELETE CASCADE 约束。与其阻止删除或留下孤立记录,这条规则会在被引用的父行被删除时,指示数据库自动移除所有依赖的子行。

在本教程中,我将展示 ON DELETE CASCADE 约束的工作原理、数据库为何支持自动级联删除,以及与更为严格的规则(如 ON DELETE RESTRICT)相比,何时是正确选择。 如果您是 SQL 新手,请先学习我们的 Introduction to SQL 课程;如果已有一定经验,可学习 Intermediate SQL 课程。 

什么是 SQL 中的 ON DELETE CASCADE?

要理解 ON DELETE CASCADE 外键约束如何工作,先来了解引用完整性和 CASCADE 子句。

引用完整性与外键

在关系型数据库中,表通过外键相连:一个表(子表)中的列链接到另一个表(父表)的主键。外键通过强制引用完整性,确保子行始终指向一个有效的父行。

如果不进行这种约束,数据库很容易出现孤立记录,即行引用了不再存在的数据。因此,删除规则的存在是为了告诉数据库在问题发生之前就如何处理这些“孤儿”。

CASCADE 的实际含义

在 SQL 中,ON DELETE CASCADE 约束会指示数据库自动删除所有引用被删父行的子行。它会沿着外键链递归触发该操作,在一次原子事务中删除依赖数据。

例如,您可以删除数据库中的某个客户记录,它会级联删除其订单,进而在一次查询中删除订单项。

ON DELETE CASCADE 的实际工作方式

现在您已了解 CASCADE 的作用,让我们看看数据库如何应用这套逻辑。

父子表关系

如您所知,数据库关系中有一个表充当父表,保存主键,另一个充当子表,将该主键作为外键存储。当您对父表执行 DELETE 语句时,数据库引擎会首先检查外键约束,通过索引来识别所有依赖项以加速处理。

删除父行时会发生什么

当您对父行执行 DELETE 命令时,数据库会在最终执行前暂停,按以下顺序检查其外键规则:

  • 识别: 引擎查看外键索引,找到子表中与被删除 ID 匹配的所有行。
  • 执行: 在移除父行之前(或期间),引擎会删除已识别到的子行。
  • 验证: 数据库确保不再有孤立行残留。若所有删除成功,事务即告完成。

需要注意的是,只有在您定义了 ON DELETE CASCADE 约束时,上述过程才会发生,我们将在后文看到。

ON DELETE CASCADE 与其他删除规则对比

在 SQL 中,不同的 DELETE 规则告诉数据库如何处理不同表之间的相关数据。本节将把 ON DELETE CASCADE 与这些约束进行比较。

CASCADE 与 RESTRICT

从以上解释可知,CASCADE 会在父行被移除时自动删除所有依赖的子行,确保不留孤立记录。

然而,SQL 的 RESTRICT 约束会在仍有子行引用该父行时完全阻止删除操作。这可确保避免意外数据丢失。下例中,RESTRICT 约束会在仍有订单引用某客户时,阻止删除该客户。

-- Parent table
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100)
);

-- Child table with ON DELETE RESTRICT
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    CONSTRAINT fk_orders_customers
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE RESTRICT
);

CASCADE 与 SET NULL、SET DEFAULT

CASCADE 会完全移除依赖行,而 SET NULL 和 SET DEFAULT 会以下述方式保留子行:

当您将约束定义为 ON DELETE SET NULL 时,会删除父行,但子表中的外键值会被设置为 NULL。

在下例中,删除客户并不会移除其关联订单。相反,orders 表中的 customer_id 列会自动设为 NULL。这保留了订单记录,同时切断了与被删客户的关系。

-- Child table using ON DELETE SET NULL
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT NULL,
    CONSTRAINT fk_orders_customers
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE SET NULL
);

类似地,使用 SET DEFAULT 选项时,删除客户也不会移除相关订单。相反,orders 表中的 customer_id 会被重置为其定义的默认值。这样可以保留订单记录,同时为其分配一个预设占位值。

-- Child table using ON DELETE SET DEFAULT
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT DEFAULT 0,
    CONSTRAINT fk_orders_customers
        FOREIGN KEY (customer_id)
        REFERENCES customers(customer_id)
        ON DELETE SET DEFAULT
);

使用 ON DELETE CASCADE 创建外键

要在 SQL 中使用 ON DELETE RESTRICT 约束,您必须在建立父子表之间的外键关系时进行指定。

在表模式中使用 CASCADE

您可以在创建表时或通过修改现有表来定义 ON DELETE CASCADE 规则。

下面的示例展示了在定义新表时如何应用 SQL 的 ON DELETE CASCADE 外键约束。使用 CASCADE 后,删除某个部门会自动移除与该部门关联的所有员工。

-- Stores department details
CREATE TABLE departments (
    department_id INT PRIMARY KEY, 
    department_name VARCHAR(100)
);

-- Automatically deletes employees when their department is deleted
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,      -- Unique employee identifier
    department_id INT, 
    CONSTRAINT fk_employees_departments
        FOREIGN KEY (department_id)
        REFERENCES departments(department_id)
        ON DELETE CASCADE
);

如果表已存在,您也可以添加带有 ON DELETE CASCADE 的外键约束。因此,当您删除部门时,相关的员工记录会自动被删除。

-- Adds a foreign key constraint after the table already exists
ALTER TABLE employees
ADD CONSTRAINT fk_employees_departments
FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE CASCADE;    -- Deletes employees when the related department is deleted

级联链与多级删除

CASCADE 的强大之处在于删除并不止于单一关系。如果某个子表同时又是另一张表的父表,删除操作可以跨多级传播,这称为级联链。例如,删除用户可能会删除订单,进而删除订单项,甚至进一步删除相关审计行。

因此,使用 CASCADE 的模式需要充分记录并仔细审查。由于单个删除语句可能影响多张表,启用级联行为之前,您应了解完整的依赖链。

ON DELETE CASCADE 的常见用例

当子表数据在没有父记录时不再具有价值时,SQL 中的 ON DELETE CASCADE 约束尤为重要。下面来看其在真实数据库管理中的应用。

自动清理依赖数据

您可以使用 ON DELETE CASCADE 来清理那些手动清理效率低下的数据。这些包括日志、历史记录、审计轨迹,或主要用于支撑父记录的多对多关联表。例如:

  • 删除用户应移除其登录历史
  • 删除订单应移除其订单项
  • 删除产品应移除产品-类别映射表中的对应条目

简化数据全生命周期管理

您也可以使用 CASCADE 将数据生命周期规则下沉到数据库中。数据库会以一致且原子化的方式强制执行这些规则,而无需依赖应用代码去查找相关记录、按正确顺序删除并处理边界情况和失败。

通过这种方式,CASCADE 确保无论删除如何触发——来自应用、脚本或管理员查询——结果始终一致。

ON DELETE CASCADE 的风险与陷阱

尽管 ON DELETE CASCADE 约束很方便,但使用不当也有风险。了解常见陷阱对于设计高效的 CASCADE 约束至关重要。

意外的大规模删除

使用 CASCADE 最常见的风险是无意的大规模删除。父表上的一条 DELETE 语句可能会在多张表中悄然移除数百甚至数千条相关记录。如果未充分理解表之间的关系,这种操作可能导致难以恢复的意外数据丢失。

最佳实践是,始终确保您能追溯父子表之间外键关系。模式设计正确时,您就会知道单次删除会影响哪些表。

复杂模式中的隐藏依赖

随着数据库规模增长,表会积累更多外键,文档也可能与实际情况脱节。在这种环境中实施 CASCADE 的挑战在于,删除可能通过深层嵌套的依赖链传播,而开发人员或管理员并未察觉。

根据我的经验,建议您将 CASCADE 与清晰的模式文档配合使用,并定期审查外键关系。

试试我们的 SQL Server for Database Administrators 技能路径,如果您的工作经常需要设计或维护数据库。 

特定数据库的注意事项与行为

虽然 ON DELETE CASCADE 的概念在各类关系型数据库中是通用的,但每种引擎在底层执行上略有差异。

PostgreSQL、MySQL 与 SQL Server 的行为

PostgreSQL、MySQL 和 SQL Server 都支持 ON DELETE CASCADE 并以相同方式实现,但有以下例外:

  • MySQL: CASCADE 在 InnoDB 中表现良好,但会避免循环级联

  • PostgreSQL: 对级联删除链最灵活、最宽松

  • SQL Server: 由于“多重级联路径”规则更安全但也更受限

事务范围与回滚安全

级联删除在与原始删除语句相同的事务中执行。如果数据库在尝试删除某个子行时遇到错误,整个操作都会失败。父行不会被删除,且已删除的子行也会被恢复。

这种事务性行为提供了重要的安全网。它允许您在提交前测试、审查或撤销级联删除。

选择合适的 ON DELETE 策略

到现在为止,您已了解选择正确的删除策略取决于数据之间的关联以及期望随时间的行为。请参考以下指南,为您的模式做出正确的 DELETE 选择。

何时应使用 CASCADE

当子数据在意义或存在上完全依赖于父数据时,您应使用 ON DELETE CASCADE 约束。例如,可用于以下场景:

  • 多对多关系中的关联表
  • 临时或派生数据,如日志或历史记录

在上述情况下,自动清理既能确保一致性,又能减少应用层所需的手动删除逻辑。

我建议学习我们的 Joining Data in SQL 课程,以了解 SQL 中不同类型的连接,以及如何处理数据库中的各种关联表。

何时应避免使用 CASCADE

然而,如果子数据具有独立价值,或出于法律、审计或分析目的必须予以保留,则应在模式中避免使用 ON DELETE CASCADE 约束。例如,您可能不应在以下场景中使用 CASCADE:

  • 财务记录和发票
  • 审计日志和合规数据,例如医疗数据。

对于这类情况,建议使用更具限制性的规则,如 RESTRICT、SET NULL,或采用应用管理的删除方式,以获得更高的控制和可见性。

结论

SQL 的 ON DELETE CASCADE 约束是一种强大的自动化工具,它在父行被删除时自动移除依赖数据,从而维护引用完整性。合理使用时,它能简化清理流程、减少手动删除逻辑,并确保在应用和脚本之间行为一致。为避免意外删除,如果您尚未完全理解数据库模式中的关系,请在使用 CASCADE 时务必谨慎。

我建议学习我们的 Database Design 课程,了解如何创建和管理数据库并选择适合您需求的 DBMS。也建议尝试我们的 Associate Data Engineer in SQL 职业路径,学习数据工程和数据仓库的基础知识。  

Allan Ouko

作者

Allan Ouko

数据科学技术写作者,具备数据分析、商业智能和数据科学的一线实践经验。我撰写以行业为导向的实用内容,涵盖 SQL、Python、Power BI、Databricks 和数据工程,并以真实的分析工作为基础。我的写作兼顾技术深度与业务影响,帮助专业人士将数据转化为自信的决策。

SQL ON DELETE CASCADE 常见问题

ON DELETE CASCADE 是先删除子行还是先删除父行?

ON DELETE CASCADE 会先删除子行以维护引用完整性。

ON DELETE CASCADE 与 RESTRICT 有何不同?

CASCADE 会自动删除依赖行,而 RESTRICT 则在存在子行时阻止删除。

ON DELETE CASCADE 与 SET NULL 有何不同?

CASCADE 会移除子行;SET NULL 会保留子行,但清空外键引用。

ON DELETE CASCADE 可以回滚吗?

是的,级联删除发生在一个事务内,提交前可以回滚。

SQL 中 ON DELETE CASCADE 是默认行为吗?

不可以。您必须在创建表或修改现有表时,在外键约束中显式定义 ON DELETE CASCADE。

主题