当前位置: 首页 > news >正文

第3节-使用表格数据-外键

摘要: 在本教程中,你将了解外键以及如何使用 PostgreSQL 外键约束为表创建外键列。

外键简介

PostgreSQL 中,外键是一个表中的一列或一组列,它引用另一个表的 主键。外键起到了连接两个表的作用。

例如,下面的 products 表有一个名为 brand_id 的外键列,该列引用 brands 表的 brand_id 列:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

包含外键列的表称为子表,而子表所引用的包含主键列的表被称为被引用表或父表。

在这个示例中,brands 表是被引用的表或父表,而 products 表是子表。

外键确保子表中的数据与父表中的相关数据相匹配。例如,如果你的 products 表中有一个品牌ID,那么该品牌ID应与 brands 表中的某个品牌ID相匹配。

外键有助于维护引用完整性。例如,如果某个产品引用了一个品牌,那么该品牌必须存在于 brands表中。

当你删除父表中的一行时,应妥善处理子表,以避免出现孤立记录。

请注意,孤立记录是指一个表中引用了另一个表中不存在的主键值的记录。

例如,如果 brands 中的一行被删除,products 表中所有相关的行都应自动删除。

一对多关系中的外键

当两个表存在一对多关系时,"多"的一方的表会包含外键列。

例如,brands 表和 products 表之间的关系是一对多。products 表是":的一方;它应该有一个外键列:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

products 表有一个名为 brand_id 的附加列,该列引用 brands 表的 brand_id

同样,categories 表和 products 表之间的关系是一对多关系。

products 表有一个名为 category_id 的外键列,该列引用了 categories 表的 category_id 列:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个表可能有多个外键列,例如 products 表。

外键列中的值等于引用行的主键中的值。例如,中的第一行的 brand_id 列等于 brand_id 列的 brands 表。

按照惯例,外键列的格式为 table_id 。例如,外键列是 category_idbrand_id

多行可以具有相同的外键列值。例如,产品名称为 iPhone 14 ProiPhone 15 Pro 的两行都具有相同的 brand_id 1。

多对多关系中的外键

PostgreSQL 中,你可以使用两个一对多关系来建模多对多关系。

例如,products 表和 tags 表之间的关系是多对多的。

为了建模这种关系,我们创建了一个名为 product_tags 的新表,该表通常被称为链接表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

product_tags 将有两个外键;一个引用 products 表中的 product_id 列,另一个引用 tags 表中的 tag_id 列。

外键约束

PostgreSQL 中,您可以使用外键约束来设置外键。

以下是定义外键约束的基本语法:

CONSTRAINT constraint_name
FOREIGN KEY (fk_column)
REFERENCES table(pk_column)
ON DELETE delete_action
ON UPDATE update_action;

在该语法中:

  • 首先,在 CONSTRAINT 子句中指定外键约束的名称。如果不指定,PostgreSQL会自动生成一个约束名称。
  • 其次,将外键列 (fk_column) 放在 FOREIGN KEY 关键字后的括号 () 内。
  • 第三,在 REFERENCES 关键字后提供外键列所引用的表和主键列。
  • 最后,设置删除和更新操作,以指定当父表中主键所在行被删除和更新时的行为。

FOREIGN KEYREFERENCES 子句是强制性的,而 CONSTRAINTON DELETEON UPDATE 子句是可选的。

外键约束示例

首先, 创建一个新表,名为 brands:

CREATE TABLE brands (brand_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(255) NOT NULL
);

其次,使用外键列 brand_id 创建一个名为产品的新表,该表引用品牌表的 brand_id 列:

CREATE TABLE products (product_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(100) NOT NULL,price DECIMAL(10, 2) NOT NULL,brand_id INT NOT NULL,FOREIGN KEY (brand_id) REFERENCES brands (brand_id)
);

第三,将三行插入 brands 表:

INSERT INTO brands(name)
VALUES ('Apple'), ('Samsung'), ('Google')
RETURNING *;

输出:

 brand_id |  name
----------+---------1 | Apple2 | Samsung3 | Google

向外键表中插入数据

如果你向 products 表中插入新行,则 brand_id 的值必须存在于 brands 表中。例如,以下语句向 products 表中插入一行新数据,其中 brand_id 为1:

INSERT INTO products(name, price, brand_id)
VALUES('iPhone 14 Pro', 999.99, 1)
RETURNING *;

输出:

 product_id |     name      | price  | brand_id
------------+---------------+--------+----------1 | iPhone 14 Pro | 999.99 |        1

如果您尝试插入品牌 ID 不存在的行,您将遇到错误 brands 表。

以下语句尝试向 products 表中插入一行新数据,但使用了无效的品牌 ID:

INSERT INTO products(name, price, brand_id)
VALUES('iPhone 15 Pro', 1299.99, 11)
RETURNING *;

PostgreSQL 会发出以下错误:

insert or update on table "products" violates foreign key constraint "products_brand_id_fkey"

删除带有外键约束的表

当您通过外键约束删除被其他表引用的表时,PostgreSQL 会发出错误。

例如,products 表通过外键约束引用 brands 表。如果删除品牌表,将会出现错误:

DROP TABLE brands;

错误:

cannot drop table brands because other objects depend on it

drop brands 表,您按照以下步骤操作:

  • 首先, 删除引用 brands 表中 brand_id 列的外键约束。
  • 其次,删除表 brands

您可以使用 DROP TABLE ... CASCADE 语句来完成这两个步骤:

DROP TABLE brands CASCADE;

此语句删除了表 products 上的约束 products_brand_id_fkey

我们也来删除 products 表:

DROP TABLE products;

删除操作

删除操作允许你在父表中的行被删除时,为子表中的行指定一个操作。

ON DELETE delete_action;

delete_action 可以是以下情况之一:

  • NO ACTION – 发出约束违反错误。NO ACTION 是默认设置。
  • SET NULL – 将外键列的值设置为 NULL
  • CASCADE – 删除子表中所有相关的行。
  • SET DEFAULT – 为子表中的外键列设置默认值。
  • RESTRICT 的作用类似于 NO ACTION

删除时设为 NULL

首先,重新创建 brands 表:

CREATE TABLE brands (brand_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(255) NOT NULL
);

其次, 创建 products 表,该表将 brand_id 作为外键列,引用 brands 表的 brand_id 列:

CREATE TABLE products (product_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(100) NOT NULL,price DECIMAL(10, 2) NOT NULL,brand_id INT,FOREIGN KEY (brand_id) REFERENCES brands (brand_id) ON DELETE SET NULL
);

第三,将行插入到 brandsproducts 表:

INSERT INTO brands(name)
VALUES ('Apple'), ('Samsung')
RETURNING *;INSERT INTO products(name, price, brand_id)
VALUES
('iPhone 14 Pro', 999.99, 1),
('iPhone 15 Pro', 1299.99, 1),
('Galaxy S23 Ultra', 1299.99, 2)
RETURNING *;

第四,在 brangs 表中 delete 一条ID 为 1 的数据:

DELETE FROM brands WHERE brand_id = 1;

最后,从 products 表中查询数据:

SELECT * FROM products;

输出:

 product_id |       name       |  price  | brand_id
------------+------------------+---------+----------3 | Galaxy S23 Ultra | 1299.99 |        21 | iPhone 14 Pro    |  999.99 |     NULL2 | iPhone 15 Pro    | 1299.99 |     NULL

输出显示,ON DELETE SET NULL 操作会将相关产品的 brand_id 列的值设置为 NULL

删除时级联

首先,删除 brandsproducts 表:

DROP TABLE brands, products;

其次,创建带有 ON DELETE CASCADE 操作的 brands 表和 products 表:

CREATE TABLE brands (brand_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(255) NOT NULL
);
CREATE TABLE products (product_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(100) NOT NULL,price DECIMAL(10, 2) NOT NULL,brand_id INT,FOREIGN KEY (brand_id) REFERENCES brands (brand_id) ON DELETE CASCADE
);

第三,向 products 表中插入行:

INSERT INTO brands(name)
VALUES('Apple'), ('Samsung')
RETURNING *;INSERT INTO products(name, price, brand_id)
VALUES
('iPhone 14 Pro', 999.99, 1),
('iPhone 15 Pro', 1299.99, 1),
('Galaxy S23 Ultra', 1299.99, 2)
RETURNING *;

第四,删除一行 来自于 brands 表的 ID1 的数据:

DELETE FROM brands 
WHERE brand_id = 1;

最后,从 products 表中查询数据:

SELECT * FROM products;

输出:

 product_id |       name       |  price  | brand_id
------------+------------------+---------+----------3 | Galaxy S23 Ultra | 1299.99 |        2

输出显示, ON DELETE CASCADE 操作会删除 products 表中 brand_id 列值为 1 的行。

删除时设置默认值

首先,删除 brandsproducts 表:

DROP TABLE brands, products;

其次,创建 brands 表:

CREATE TABLE brands (brand_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,name VARCHAR(255) NOT NULL
);

第三,创建 products 表:

CREATE TABLE products (product_id INT GENERATED ALWAYS AS IDENTITY  PRIMARY KEY,name VARCHAR(100) NOT NULL,price DECIMAL(10, 2) NOT NULL,brand_id INT DEFAULT 1,FOREIGN KEY (brand_id) REFERENCES brands (brand_id) ON DELETE SET DEFAULT
);

在这条语句中,我们将 brand_id 列的默认值设置为 1

第四,向 brands 表中插入行:

INSERT INTO brands(name)
VALUES('Unknown'),('Apple'), ('Samsung')
RETURNING *;

ID1 的行将是"Unknown"。

第五,向 products 表中插入行:

INSERT INTO products(name, price, brand_id)
VALUES
('iPhone 14 Pro', 999.99, 2),
('iPhone 15 Pro', 1299.99, 2),
('Galaxy S23 Ultra', 1299.99, 3)
RETURNING *;

第六,从 brands 表中删除 id2 的行:

DELETE FROM brands 
WHERE brand_id = 2;

由于 ON DELETE SET DEFAULT 操作,该语句会将 brand_id2 的行的 brand_id 列的值设置为 1

第七,从 products 表中查询数据:

SELECT * FROM products;

输出:

 product_id |       name       |  price  | brand_id
------------+------------------+---------+----------3 | Galaxy S23 Ultra | 1299.99 |        31 | iPhone 14 Pro    |  999.99 |        12 | iPhone 15 Pro    | 1299.99 |        1

输出显示,ON DELETE SET DEFAULT 操作会将相关产品的 brand_id 列的值设置为 brand_id 列的默认值,即 1

总结

  • 外键是一个或一组列,用于引用另一个表的主键。
  • 个表可以有一个或多个外键列。
  • 使用 PostgreSQL 外键约束为表创建外键。
  • 当从父表中删除相应的行时,使用 ON DELETE SET NULL 将外键列的值设置为 NULL
  • 使用 ON DELETE CASCADE 可以在删除父表中相应行时,删除子表中的行。
  • 当父表中的对应行被删除时,使用 ON DELETE SET DEFAULT 将外键列的值设置为其默认值。
  • 使用 ON DELETE NO ACTION 可以防止当父表中的行在子表中有引用行时被删除。

文章转载自:

http://0TnVOdZU.qpfmh.cn
http://Z0iNDlrb.qpfmh.cn
http://qBLf5wGK.qpfmh.cn
http://LxqbLdaP.qpfmh.cn
http://a4Kl66cm.qpfmh.cn
http://NO24K5wN.qpfmh.cn
http://FJvlzvsO.qpfmh.cn
http://QaaVcrhe.qpfmh.cn
http://v8RjuTnb.qpfmh.cn
http://awddCQeX.qpfmh.cn
http://WZCjv6W7.qpfmh.cn
http://RbjRQPPf.qpfmh.cn
http://e7uy5Gwd.qpfmh.cn
http://ASkUJ2VH.qpfmh.cn
http://SBTKqbPL.qpfmh.cn
http://ovdgLVol.qpfmh.cn
http://YpvZWEei.qpfmh.cn
http://cRMOZkz7.qpfmh.cn
http://lUV3Zpak.qpfmh.cn
http://Ep5tsQIy.qpfmh.cn
http://N5cqPpjw.qpfmh.cn
http://Gh9I5bBV.qpfmh.cn
http://IRjtH4wY.qpfmh.cn
http://gOi9bnQw.qpfmh.cn
http://iFWzdipL.qpfmh.cn
http://0REaNEZf.qpfmh.cn
http://ByhNQ2S8.qpfmh.cn
http://F51U3s1e.qpfmh.cn
http://x4utXyT7.qpfmh.cn
http://h04fMcOS.qpfmh.cn
http://www.dtcms.com/a/377063.html

相关文章:

  • 面试题: Mysql中的深分页如何处理
  • OpenCV 图像直方图
  • 【51单片机】【protues仿真】基于51单片机智能路灯PCF8591系统
  • 虚拟局域网(VLAN)入门指南:打破物理界限的网络划分术
  • 【HD-RK3576-PI】LoRa无线串口模块
  • 自动驾驶中的传感器技术42——Radar(3)
  • kafka消息积压出现的原因、危害及解决方案
  • 《sklearn机器学习——数据预处理》非线性转换
  • 登顶 NAVSIM!博世最新IRL-VLA:逆强化学习重构自动驾驶VLA闭环训练
  • 速度与安全双突破:大视码垛机重构工业自动化新范式​
  • Java全栈开发面试实录:从基础到微服务的深度解析
  • 智慧养老:科技的温度,生命的尊严——构建银发时代的幸福图景
  • 【SpringBoot3】与myBatis-plus不兼容解决
  • 阿尔泰科技ARTS-3002U USB总线多功能数据采集卡 技术解析
  • Java 教程:轻松实现 Excel 与 CSV 互转 (含批量转换)
  • 行业学习【电商】:订阅制电商
  • 【Halcon】Halcon HObject 转 Bitmap 的几种实现方法
  • 单片机启动文件——数据段重定位,BSS段清零
  • [xboard]ARM汇编基础学习
  • rv1126bp之mipi sensor驱动
  • 手机上可以记录每日工作计划的待办提醒工具?
  • 今天开始我们学习安全管理模块Linux防火墙
  • 反爬API接口:技术实现与应用场景
  • 10.3 马尔可夫矩阵、人口和经济
  • OpenResty 中实现限流(Rate Limiting)的实战案例
  • 告别“人肉API”时代:AI智能体如何重构人机协同新范式
  • Centos7部署ceph存储
  • 【Pywinauto库】10.1 pywinauto.base_wrapper控件
  • 机器人/人形机器人无法商业化落地的原因
  • 十一旅游气象的关键影响与“用大模型拿到更好天气数据”的落地路线