📜  PostgreSQL – 外键

📅  最后修改于: 2022-05-13 01:57:15.243000             🧑  作者: Mango

PostgreSQL – 外键

在本文中,我们将使用 SQL 语句研究 PostgreSQL 外键约束。外键是用于唯一标识不同表的行的一列或一组列。包含外键的表称为引用表或子表。外键引用的表称为被引用表或父表。根据与其他表的关系,一个表可以拥有多个外键。

Syntax:  FOREIGN KEY (column) REFERENCES parent_table (table_name)

我们来分析一下上面的语法:

  • 首先,在 CONSTRAINT关键字后指定外键约束的名称。约束子句是可选的。如果省略它,PostgreSQL 将分配一个自动生成的名称。
  • 其次,在 FOREIGN KEY关键字后的括号中指定一个或多个外键列。
  • 第三,在REFERENCES子句中指定外键列所引用的父表和父键列。
  • 最后,在 ON DELETEON 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 DELETEON 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 DEFERREDINITIALLY 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;