PostgreSQL – 外键
在本文中,我们将使用 SQL 语句研究 PostgreSQL 外键约束。外键是用于唯一标识不同表的行的一列或一组列。包含外键的表称为引用表或子表。外键引用的表称为被引用表或父表。根据与其他表的关系,一个表可以拥有多个外键。
Syntax: FOREIGN KEY (column) REFERENCES parent_table (table_name)
我们来分析一下上面的语法:
- 首先,在 CONSTRAINT关键字后指定外键约束的名称。约束子句是可选的。如果省略它,PostgreSQL 将分配一个自动生成的名称。
- 其次,在 FOREIGN KEY关键字后的括号中指定一个或多个外键列。
- 第三,在REFERENCES子句中指定外键列所引用的父表和父键列。
- 最后,在 ON DELETE和ON UPDATE子句中指定删除和更新操作。
删除和更新操作决定了删除和更新父表中的主键时的行为。由于主键很少更新,因此ON UPDATE 操作在实践中并不经常使用。我们将专注于ON DELETE操作。
PostgreSQL 支持以下操作:
- 置空
- 默认设置
- 严格
- 没有行动
- 级联
例子:
以下语句创建客户和联系人表:
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)
);
在这个例子中,customer 表是父表,contacts 表是子表。每个客户有零个或多个联系人,每个联系人属于零个或一个客户。联系人表中的customer_id列是引用客户表中同名主键列的外键列。联系人表中的以下外键约束fk_customer将 customer_id定义为外键:
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
因为外键约束没有ON DELETE和ON UPDATE操作,所以它们默认为NO ACTION 。
没有行动
以下将数据插入到客户和联系人表中:
INSERT INTO customers(customer_name)
VALUES('GeeksforGeeks org'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1, 'Raju kumar', '(408)-111-1234', 'raju.kumar@geeksforgeeks.org'),
(1, 'Raju kumar', '(408)-111-1235', 'raju.kumar@bluebird.dev'),
(2, 'Nikhil Aggarwal', '(408)-222-1234', 'nikhil.aggarwalt@geeksforgeeks.org');
以下语句从客户表中删除客户 ID 1:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE NO ACTION ,PostgreSQL 发出约束冲突,因为客户 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
的制约作用是类似的任何行动。仅当您使用INITIALLY DEFERRED或INITIALLY IMMEDIATE模式将外键约束定义为DEFERRABLE 时,才会出现差异。
置空
当父表中的引用行被删除时,ON DELETE CASCADE 自动将 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('GeeksforGeeks org'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1, 'Raju kumar', '(408)-111-1234', 'raju.kumar@geeksforgeeks.org'),
(1, 'Raju kumar', '(408)-111-1235', 'raju.kumar@bluebird.dev'),
(2, 'Nikhil Aggarwal', '(408)-222-1234', 'nikhil.aggarwalt@geeksforgeeks.org');
以下语句将数据插入到客户和联系人表中:
INSERT INTO customers(customer_name)
VALUES('GeeksforGeeks org'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1, 'Raju kumar', '(408)-111-1234', 'raju.kumar@geeksforgeeks.org'),
(1, 'Raju kumar', '(408)-111-1235', 'raju.kumar@bluebird.dev'),
(2, 'Nikhil Aggarwal', '(408)-222-1234', 'nikhil.aggarwalt@geeksforgeeks.org');
要查看SET NULL是如何工作的,让我们从customer表中删除 id 为 1 的客户:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE SET NULL 操作,联系人表中的引用行设置为 NULL。以下语句显示了联系人表中的数据:
SELECT * FROM contacts;
这将导致以下结果:
从输出中可以清楚地看出,具有customer_id 的行 1 现在有customer_id 设置为NULL
级联
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('GeeksforGeeks org'),
('Dolphin LLC');
INSERT INTO contacts(customer_id, contact_name, phone, email)
VALUES(1, 'Raju kumar', '(408)-111-1234', 'raju.kumar@geeksforgeeks.org'),
(1, 'Raju kumar', '(408)-111-1235', 'raju.kumar@bluebird.dev'),
(2, 'Nikhil Aggarwal', '(408)-222-1234', 'nikhil.aggarwalt@geeksforgeeks.org');
以下语句删除客户 ID 1:
DELETE FROM customers
WHERE customer_id = 1;
由于ON DELETE CASCADE 操作,联系人表中的所有引用行都将自动删除:
SELECT * FROM contacts;
这将导致以下结果:
默认设置
当从父表中引用的行被删除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;