九月 13, 2023
摘要:在本教程中,您将了解 PostgreSQL 外键以及如何使用外键约束向表添加外键。
目录
PostgreSQL 外键约束简介
外键是表中引用另一个表的主键的一列或一组列。
包含外键的表称为引用表或子表。而外键引用的表称为被引用表或父表。
一个表可以有多个外键,具体取决于它与其他表的关系。
在 PostgreSQL 中,您可以使用外键约束来定义外键。外键约束有助于维护子表和父表之间数据的引用完整性。
外键约束表示子表中的一列或一组列中的值等于父表中的一列或一组列中的值。
PostgreSQL 外键约束语法
下面说明了外键约束语法:
[CONSTRAINT fk_name]
FOREIGN KEY(fk_columns)
REFERENCES parent_table(parent_key_columns)
[ON DELETE delete_action]
[ON UPDATE update_action]
在这个语法中:
- 首先,在
CONSTRAINT
关键字后指定外键约束的名称。CONSTRAINT
子句是可选的。如果省略它,PostgreSQL 将分配一个自动生成的名称。 - 其次,在
FOREIGN KEY
关键字后面的括号中指定一个或多个外键列。 - 第三,在
REFERENCES
子句中指定外键列引用的父表和父键列。 - 最后,在
ON DELETE
和ON UPDATE
子句中指定删除和更新操作。
删除和更新操作决定了父表中的主键被删除和更新时的行为。由于主键很少更新,因此ON UPDATE action
在实际中并不经常被用到。我们将重点关注ON DELETE action
。
PostgreSQL 支持以下操作:
- SET NULL
- SET DEFAULT
- RESTRICT
- NO ACTION
- CASCADE
PostgreSQL 外键约束示例
以下语句创建customers
和contacts
表:
DROP TABLE IF EXISTS customers;
DROP TABLE IF EXISTS contacts;
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL,
phone VARCHAR(15),
email VARCHAR(100),
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
);
在本例中,customers
表是父表,contacts
表是子表。
每个客户有零个或多个联系方式,每个联系方式属于零个或一个客户。
contacts
表中的customer_id
列是引用customers
表中同名主键列的外键列。
contacts
表中的以下外键约束fk_customer
将customer_id
定义为外键:
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
由于该外键约束没有指定ON DELETE
和ON UPDATE
操作,因此它默认为NO ACTION
。
NO ACTION
以下将数据插入到customers
和contacts
表中:
INSERT INTO customers(customer_name)
VALUES('BlueBird Inc'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'),
(1,'Jane Doe','(408)-111-1235','jane.doe@bluebird.dev'),
(2,'David Wright','(408)-222-1234','david.wright@dolphin.dev');
以下语句从customers
表中删除 ID 为 1 的客户:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE NO ACTION
,PostgreSQL 会发出约束冲突,因为contacts
表中仍然存在客户 ID 为 1 的引用行:
ERROR: update or delete on table "customers" violates foreign key constraint "fk_customer" on table "contacts"
DETAIL: Key (customer_id)=(1) is still referenced from table "contacts".
SQL state: 23503
RESTRICT
动作与NO ACTION
类似。仅当您将外键约束定义为INITIALLY DEFERRED
或INITIALLY IMMEDIATE
模式的DEFERRABLE
时,才会出现差异。我们将在后续教程中对此进行更多讨论。
SET NULL
当删除父表中引用的行时,SET NULL
自动设置子表引用行中的外键列为NULL
。
以下语句删除示例表,并使用ON DELETE SET NULL
子句定义的外键,重新创建它们:
DROP TABLE IF EXISTS contacts;
DROP TABLE IF EXISTS customers;
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL,
phone VARCHAR(15),
email VARCHAR(100),
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
ON DELETE SET NULL
);
INSERT INTO customers(customer_name)
VALUES('BlueBird Inc'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'),
(1,'Jane Doe','(408)-111-1235','jane.doe@bluebird.dev'),
(2,'David Wright','(408)-222-1234','david.wright@dolphin.dev');
以下语句将数据插入到customers
和contacts
表中:
INSERT INTO customers(customer_name)
VALUES('BlueBird Inc'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'),
(1,'Jane Doe','(408)-111-1235','jane.doe@bluebird.dev'),
(2,'David Wright','(408)-222-1234','david.wright@dolphin.dev');
要了解SET NULL
的工作原理,让我们从customers
表中删除 ID 为 1 的客户:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE SET NULL
操作,contacts
表中的引用行设置为 NULL。以下语句显示contacts
表中的数据:
SELECT * FROM contacts;
从输出中可以清楚地看到,customer_id
为 1 的行现在的customer_id
列值已设置成NULL
。
CASCADE
当删除父表中的引用行时,ON DELETE CASCADE
会自动删除子表中的所有引用行。在实践中,ON DELETE CASCADE
是最常用的选项。
以下语句重新创建示例表。但是,外键约束fk_customer
的删除操作更改为CASCADE
:
DROP TABLE IF EXISTS contacts;
DROP TABLE IF EXISTS customers;
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL,
phone VARCHAR(15),
email VARCHAR(100),
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
ON DELETE CASCADE
);
INSERT INTO customers(customer_name)
VALUES('BlueBird Inc'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'),
(1,'Jane Doe','(408)-111-1235','jane.doe@bluebird.dev'),
(2,'David Wright','(408)-222-1234','david.wright@dolphin.dev');
以下语句删除 ID 为 1 的客户:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE CASCADE
操作,contacts
表中的所有引用行都会自动删除:
SELECT * FROM contacts;
SET DEFAULT
当从父表中删除引用行时,ON DELETE SET DEFAULT
将子表中引用行的外键列设置为默认值。
向现有表添加外键约束
要将外键约束添加到现有表,请使用以下形式的 ALTER TABLE 语句:
ALTER TABLE child_table
ADD CONSTRAINT constraint_name
FOREIGN KEY (fk_columns)
REFERENCES parent_table (parent_key_columns);
当您向现有表添加带ON DELETE CASCADE
选项的外键约束时,需要执行以下步骤:
首先,删除现有的外键约束:
ALTER TABLE child_table
DROP CONSTRAINT constraint_fkey;
其次,使用 ON DELETE CASCADE
操作添加新的外键约束:
ALTER TABLE child_table
ADD CONSTRAINT constraint_fk
FOREIGN KEY (fk_columns)
REFERENCES parent_table(parent_key_columns)
ON DELETE CASCADE;
在本教程中,您了解了 PostgreSQL 外键以及如何使用外键约束为表创建外键。