想象一下,你建了一个数据库,有一个客户表(customers)和跟客户相关的订单表(orders)。但有时候会遇到这样的问题:如果某个客户从 customers 表里被删了怎么办?这个客户的订单也要一起删掉,还是让它们变成“孤儿”,指向一个不存在的客户?还有,如果你想改客户的 ID 呢?这时候就轮到级联操作(CASCADE)和限制(RESTRICT)来帮你控制数据库的行为了。
ON DELETE CASCADE —— 这个机制会在你从父表删掉一条记录时,自动把所有相关的子表记录也删掉。换句话说,如果你删了一个客户,所有和他有关的订单也会被删掉。
它的工作方式是这样的。当你在外键定义里加上 ON DELETE CASCADE,数据库就“明白”了,相关的记录也要自动删掉。
例子
假设我们有两张表:customers 和 orders。一个客户 customers 可以有多个订单 orders,这就是 ONE-TO-MANY 的关系。我们想要的是,当客户被删时,他的所有订单也跟着删掉。
-- 创建客户表
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
-- 创建订单表,外键指向客户表
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE,
order_date DATE NOT NULL
);
往表里插入数据
-- 往客户表插入数据
INSERT INTO customers (name) VALUES ('伊万'), ('安娜');
-- 往订单表插入数据
INSERT INTO orders (customer_id, order_date) VALUES
(1, '2023-10-01'),
(1, '2023-10-02'),
(2, '2023-10-03');
orders 表:
| order_id | customer_id | order_date |
|---|---|---|
| 1 | 1 | 2023-10-01 |
| 2 | 1 | 2023-10-02 |
| 3 | 2 | 2023-10-03 |
删除客户,看看会发生什么
-- 删除 ID 为 1 的客户
DELETE FROM customers WHERE customer_id = 1;
-- 检查订单表里还剩下什么
SELECT * FROM orders;
| order_id | customer_id | order_date |
|---|---|---|
| 3 | 2 | 2023-10-03 |
可以看到,和被删客户相关的订单也都被删掉了。
限制更改:ON UPDATE RESTRICT
ON UPDATE RESTRICT 可以阻止你更改父表里的值,如果子表里有记录还在引用这个值。它就像一个“保护墙”,防止那些可能破坏数据完整性的更改。
怎么用?当你加上 ON UPDATE RESTRICT,如果子表有记录引用这个主键,数据库就不让你改父表的键。
例子
我们还是用 customers 和 orders 这两张表,不过这次在外键上加上了更新限制。
-- 重新创建订单表,加上更新限制
DROP TABLE orders;
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON UPDATE RESTRICT,
order_date DATE NOT NULL
);
试着更改客户 ID
-- 尝试把客户 ID 从 2 改成 5
UPDATE customers
SET customer_id = 5
WHERE customer_id = 2;
结果:
ERROR: update or delete on table "customers" violates foreign key constraint
DETAIL: Key (customer_id)=(2) is still referenced from table "orders".
你看,数据库直接报错了,因为更改主键会破坏表之间的关联。
关于 UPDATE 和它的细节,下一级我会再详细讲 :P
结合 ON DELETE CASCADE 和 ON UPDATE RESTRICT
当然,级联操作(CASCADE)和限制(RESTRICT)可以一起用。比如,你可以设置成删除父表记录时自动删掉相关数据(ON DELETE CASCADE),但禁止更改它的 ID(ON UPDATE RESTRICT),这样可以避免一些意外后果。
例子
我们再来创建一次订单表,这次两个机制都用上:
DROP TABLE orders;
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id)
ON DELETE CASCADE
ON UPDATE RESTRICT,
order_date DATE NOT NULL
);
现在:
- 如果你删了客户,他的所有订单都会被删掉。
- 如果你试图更改客户的 ID,会报错。
为什么在实际项目中很重要?
用 CASCADE 和 RESTRICT 对于有很多关联表的大型系统来说非常重要。比如:
在电商网站里,一个客户可以有订单。如果客户决定删除自己的账号,你肯定不想让数据库里留下那些已经没有任何关联的订单。这时候 ON DELETE CASCADE 就很有用。
同时,你可能还想防止误操作导致唯一键被改掉,破坏表之间的关联。这个时候 ON UPDATE RESTRICT 就能帮上忙。
GO TO FULL VERSION