MySQL第五讲·关于外键和连接, 如何做到关联查询?

news2024/11/26 1:51:21

在这里插入图片描述

你好,我是安然无虞。

文章目录

  • 外键和连接:如何做关联查询?
    • 如何创建外键?
    • 连接
    • 关联查询中的误区

外键和连接:如何做关联查询?

在实际的数据库应用开发过程中,我们经常需要把2个或2个以上的表进行关联,以获取需要的数据。这是因为,为了提高存取效率,我们会把不同业务模块的信息分别存放在不同的表里面。

但是,从业务层面上看,我们需要完整全面的信息为经营决策提供数据支撑。

还是以超市项目为例,数据库里面的销售流水表一般只保存销售必需的信息,比如商品编号,数量, 价格,金额和会员卡号等。但是,在呈现给超市经营者的统计报表中,只包括这些信息是不够的,比如商品编号,会员卡号,这些数字经营者就看不懂。

因此,必须要从商品信息表中提取出商品信息,从会员表中提取出会员信息,这样才能形成一个完整的报表。这种把分散在多个不同的表里的数据查询出来的操作,就是多表查询。

不过,多表查询可不简单,我们需要建立起多个表之间的关联,然后才能去查询,同时还需要规避关联表查询中的常见错误。具体该怎么做呢,请接着看:

超市项目中的进货模块,有两个这样的数据表,分别是进货单头表(importhead)和进货单明细表(importdetailes),我们每天都需要对这两张表进行CRUD操作。

进货单头表记录的是整个进货单的总体信息:

img

进货单明细表记录的是每次进货的商品明细。一条进货单头数据记录,对应多条进货商品的明细数据,也就是所谓的一对多的关系,具体如下表所示:

img

现在我们需要查询一次进货的所有相关数据,包括进货单的总体信息和进货商品的明细,这样一来,我们就需要把2个表关联起来,该如何进行操作呢?

在MySQL中,为了把2个表关联起来,会用到2个重要功能,分别是外键(foreign key)和连接(join)。

外键需要在创建表的阶段就定义,连接可以通过相同意义的字段把2个表连接起来,用在查询阶段。

如何创建外键?

首先我们来了解一下什么是外键?

假如我们有2个表,分别是表A和表B,它们通过一个公共字段id发生关联关系,我们把这个关联关系叫做R。如果id在表A中是主键,那么表A就是这个关系R中的主表。相应的,表B就是这个关系中的从表,表B中的id,就是表B用来引用表A中数据的,叫外键。

所以,外键就是从表中用来引用主表中数据的那个公共字段。

为了方便理解,请看下图:

在这里插入图片描述

在MySQL中,外键是通过外键约束来定义的。外键约束就是约束的一种,它必须在从表中定义,包括指明哪个是外键字段,以及外键字段所引用的主表中的主键字段是什么。

MySQL系统会根据外键约束的定义,监控对主表中数据的删除操作。如果发现要删除的主表记录,正在被从表中某条记录的外键字段所引用,MySQL就会提示错误,从而确保了关联数据不会缺失。

外键约束可以在创建表的时候定义,也可以通过修改表来定义。语法结构如下:

[CONSTRAINT <外键约束名称>] FOREIGN KEY 字段名
REFERENCES <主表名> 字段名

我们可以在创建表的时候定义外键约束:

CREATE TABLE 从表名
(
  字段名 类型,
  ...
-- 定义外键约束,指出外键字段和参照的主表字段
CONSTRAINT 外键约束名
FOREIGN KEY (字段名) REFERENCES 主表名 (字段名)
)

当然,我们也可以通过修改表来定义外键约束:

ALTER TABLE 从表名 ADD CONSTRAINT 约束名 FOREIGN KEY 字段名 REFERENCES 主表名 (字段名);

一般情况下,表与表的关联都是提前设计好了的,因此,会在创建表的时候就把外键约束定义好。不过,如果需要修改表的设计,比如添加新的字段,增加新的关联关系,但是没有预先定义外键约束,那么,就要用修改表的方式来补充定义。

下面,我们来看看怎么创建外键约束:

先创建主表demo.importhead:

create table demo.importhead
(
	listnumber int primary key,
  	supplierid int,
  	stocknumber int, 
  	importtype int,
  	importquantity decimal(10, 3),
  	importvalue decimal(10, 2),
  	recoder int,
  	recodingdate datetime
);

然后创建从表demo.importdetails,并且给它定义外键约束:

create table demo.importdetails
(
	listnumber int,
  	itemnumber int,
  	quantity decimal(10,3),
  	importprice decimal(10,2),
  	importvalue decimal(10,2),
  	-- 定义外键约束,指出外键字段和参照的主表字段
  	constraint fk_importdetails_importhead
  	foreign key (listnumber) references importhead(listnumber)
);

运行这个SQL语句,我们就在创建表的同时定义了一个名字叫"fk_importdetails_importhead"的外键约束。同时,我们声明,这个外键约束的字段"listnumber"引用的是表importhead里面的字段"listnumber"

我们可以通过MySQL自带的、用于存储系统信息的数据库:information_schema来查看外键约束的相关信息:

mysql> SELECT
    ->     constraint_name, -- 表示外键约束名称
    ->     table_name, -- 表示外键约束所属数据表的名称
    ->     column_name, -- 表示外键约束的字段名称
    ->     referenced_table_name, -- 表示外键约束所参照的数据表名称
    ->     referenced_column_name -- 表示外键约束所参照的字段名称
    -> FROM
    ->     information_schema.KEY_COLUMN_USAGE
    -> WHERE
    ->     constraint_name = 'fk_importdetails_importhead';
+-----------------------------+---------------+-------------+-----------------------+------------------------+
| CONSTRAINT_NAME             | TABLE_NAME    | COLUMN_NAME | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME |
+-----------------------------+---------------+-------------+-----------------------+------------------------+
| fk_importdetails_importhead | importdetails | listnumber  | importhead            | listnumber             |
+-----------------------------+---------------+-------------+-----------------------+------------------------+
1 row in set (0.05 sec)

通过查询,我们可以看到,外键约束所在的表是“importdetails”,外键字段是“listnumber”,参照的主表是“importhead”,参照的主表字段是“listnumber”。这样,通过定义外键约束,我们已经建立起了 2 个表之间的关联关系。

关联关系建立起来之后,如何才能获取我们需要的数据呢?这时,我们就需要用到连接查询了。

连接

在MySQL中,有2种类型的连接,分别是内连接(inner join)和外连接(outer join)。

  • 内连接表示查询结果只返回符合连接条件的记录,这种连接方式比较常用;
  • 外连接则不同,表示查询结果返回某一个表中的所有记录,以及另一个表中满足连接条件的记录。

下面我们来认识一下内连接:

在MySQL中,关键字join,inner join,cross join的含义是一样的,都表示内连接。我们通过join把两个表关联起来,来查询两个表中的数据。

超市的项目中有会员销售的需求,所以我们的流水表中的数据记录,既包括非会员的普通销售,又包括会员销售。它们的区别是会员销售的数据记录包括会员编号,而在非会员销售的数据记录中,会员编号为空。

下面是销售表demo.trans,实际的销售表比较复杂,为了方便理解,对表进行了简化,并且假设业务字段cardno是会员信息表的主键,简化以后的结构如下:

img

再看一下简化后的会员信息表:

img

这两个表之间存在关联关系,表demo.trans中的字段cardno是这个关联关系中的外键。

我们可以通过内连接,查询所有会员销售的流水记录:

select 
	a.transactionno,
	a.itemnumber,
	a.quantity,
	a.price,
	a.transdate,
	b.membername
from 
	demo.trans as a
join 
	demo.membermaster as b 
on (a.cardno = b.cardno);

我们通过公共字段cardno把两个表关联到一起,查询出了会员消费的数据。

在这里,关键字join和关键字on配对使用,意思是查询满足关联条件“demo.trans”表中cardno的值与demo.membermaster表中的cardio值相等的两个表中的所有记录。

上述内容讲解的是内连接,下面我们再说说外连接。

跟内连接只返回符合连接条件的数据记录不同的是,外连接还可以返回表中的所有记录,它包括两类,分别是左连接和右连接。

  • 左连接:一般简写成left join,返回左表中的所有数据记录,以及右表中符合连接条件的记录;
  • 右连接:一般简写成right join,返回右表中的所有数据记录,以及左表中符合连接条件的记录。

当我们需要查询全部流水信息的时候就会用到外连接,代码如下:

select 
	a.transactionno,
	a.itemnumber,
	a.quantity,
	a.price,
	a.transdate,
	b.membername
from demo.trans as a
left join demo.membermaster as b -- left join以demo.trans为主
on (a.cardno = b.cardno);

可以看到,我用到了 LEFT JOIN,意思是以表 demo.trans 中的数据记录为主,这个表中的数据记录要全部出现在结果集中,同时给出符合连接条件(a.cardno=b.cardno) 的表 demo.membermaster 中的字段 membername 的值。

我们也可以使用 RIGHT JOIN 实现同样的效果,代码如下:

select 
	a.transactionno.
	a.itemnumber,
	a.quantity,
	a.price,
	a.transdate,
	a.membername
from 
	demo.membermaster as b
right join 
	demo.joins as a -- right join, 顺序颠倒了,还是以demo.trans为主
on (a.cardno = b.cardno);

运行之后查看结果:

mysql> SELECT
    ->     a.transactionno,
    ->     a.itemnumber,
    ->     a.quantity,
    ->     a.price,
    ->     a.transdate,
    ->     b.membername
    -> FROM
    ->     demo.trans AS a
    ->         LEFT JOIN   -- 左连接
    ->     demo.membermaster AS b ON (a.cardno = b.cardno);
+---------------+------------+----------+-------+---------------------+------------+
| transactionno | itemnumber | quantity | price | transdate           | membername |
+---------------+------------+----------+-------+---------------------+------------+
|             1 |          1 |    1.000 | 89.00 | 2020-12-01 00:00:00 | 张三       |
|             2 |          2 |    1.000 | 12.00 | 2020-12-02 00:00:00 | NULL       |
+---------------+------------+----------+-------+---------------------+------------+
2 rows in set (0.00 sec)

mysql> SELECT
    ->     a.transactionno,
    ->     a.itemnumber,
    ->     a.quantity,
    ->     a.price,
    ->     a.transdate,
    ->     b.membername
    -> FROM
    ->     demo.membermaster AS b
    ->         RIGHT JOIN   -- 右连接
    ->     demo.trans AS a
    ->     ON (a.cardno = b.cardno);
+---------------+------------+----------+-------+---------------------+------------+
| transactionno | itemnumber | quantity | price | transdate           | membername |
+---------------+------------+----------+-------+---------------------+------------+
|             1 |          1 |    1.000 | 89.00 | 2020-12-01 00:00:00 | 张三       |
|             2 |          2 |    1.000 | 12.00 | 2020-12-02 00:00:00 | NULL       |
+---------------+------------+----------+-------+---------------------+------------+
2 rows in set (0.00 sec)

关联查询中的误区

有了连接,我们就可以进行两个表的关联查询了。有个问题:

关联查询必须在外键约束的基础上,才可以吗?

其实,在MySQL中,外键约束不是关联查询的必要条件。很多人往往在设计表的时候,觉得只要连接查询就可以搞定一切了,外键约束太麻烦,没有必要,这样想的话,就进入了一个误区。

还是以超市进货的例子为例,假设一次进货数据是这样的:供货商编号是1,进货仓库编号是1。我们进货的商品编号是1234,进货数量是1,进货价格是10,进货金额是10.

insert into demo.importhead
(
listnumber,
supplierid,
stocknumber,
)
values
(
1234,
1,
1
);

运行SQL,查看表中内容:

mysql> SELECT *
    -> FROM demo.importhead;
+------------+------------+-------------+------------+----------+-------------+-------------+
| listnumber | supplierid | stocknumber | importtype | quantity | importprice | importvalue |
+------------+------------+-------------+------------+----------+-------------+-------------+
|       1234 |          1 |           1 |          1 |     NULL |        NULL |        NULL |
+------------+------------+-------------+------------+----------+-------------+-------------+
1 row in set (0.00 sec)

可以看到,我们有了一个进货单头,单号是 1234,供货商是 1 号供货商,进货仓库是 1 号仓库。

接着,我们向进货单明细表中插入进货明细数据:

insert into demo.importdetails
(
listnumber,
itemnumber,
quantity,
importprice,
importvalue
)
values
(
1234,
1,
1,
10,
10
);

运行SQL,查看表中记录:

mysql> SELECT *
    -> FROM demo.importdetails;
+------------+------------+----------+-------------+-------------+
| listnumber | itemnumber | quantity | importprice | importvalue |
+------------+------------+----------+-------------+-------------+
|       1234 |          1 |    1.000 |       10.00 |       10.00 |
+------------+------------+----------+-------------+-------------+
1 row in set (0.00 sec)

这样,我们就有了 1234 号进货单的明细数据:进货商品是 1 号商品,进货数量是 1 个,进货价格是 10 元,进货金额是 10 元。

这个时候,如果我删除进货单头表的数据,就会出现只有明细、没有单头的数据缺失情况。我们来看看会发生什么:

delete from demo.importhead
where listnumbere = 1234;

运行这条SQL语句,MySQL会提示错误,因为数据删除违反了外键约束。MySQL阻止了数据不一致的情况出现。

还有一个问题是,我插入数据的顺序,为什么我要先插入进货单头表的数据,再插入进货单明细表的数据呢?如果我先插入数据到从表(进货单明细表),会导致MySQL找不到参照的主表信息,会提示错误,因为添加数据违反了外键约束。

所以,虽然我们不用外键约束,也可以进行关联查询,但是有了它,MySQL系统才会保护我们的数据,避免出现误删的情况,从而提高系统整体的可靠性。

外键约束,可以帮助我们确定从表中的外键字段与主表中的主键字段之间的引用关系,还可以确保从表中数据所引用的主表数据不会被删除,从而保证了 2 个表中数据的一致性。

为什么在 MySQL 里,没有外键约束也可以进行关联查询呢?

原因是外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适合。比如大型网站的中央数据库,可能会因为外键约束的系统开销而变得非常慢。所以,MySQL 允许你不使用系统自带的外键约束,在应用层面完成检查数据一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。

所以我们要尽量养成在关联表中定义外键约束的习惯。不过,如果业务场景因为高并发等原因,无法承担外键约束的成本,也可以不定义外键约束,但是一定要在应用层面实现外键约束的逻辑功能,这样才能确保系统的正确可靠。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1177887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言函数初使用

目录 1知识点&#xff1a; 2一个小代码&#xff0c;后续知识点讲解&#xff1a; 3知识点&#xff1a; 定义函数 实例 函数声明 调用函数 函数参数 4总结&#xff1a; 1知识点&#xff1a; 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;…

网站源码备份 [极客大挑战 2019]PHP1

打开题目 题目提示我们备份网站 我们输入/www.zip 下载zip文件&#xff0c;打开发现 打开index.php <?phpinclude class.php;$select $_GET[select];$resunserialize($select);?> 文件包含class.php&#xff0c;get传参一个select函数&#xff0c;反序列化select参…

HMM与LTP词性标注之命名实体识别与HMM

文章目录 知识图谱介绍NLP应用场景知识图谱&#xff08;Neo4j演示&#xff09;命名实体识别模型架构讲解HMM与CRFHMM五大要素&#xff08;两大状态与三大概率&#xff09;HMM案例分享HMM实体识别应用场景代码实现 知识图谱介绍 NLP应用场景 图谱的本质&#xff0c;就是把自然…

JS逆向爬虫---请求参数加密②【某麦数据analysis参数加密】

主页链接: https://www.qimai.cn/rank analysis逆向 完整参数生成代码如下&#xff1a; const {JSDOM} require(jsdom) const dom new JSDOM(<!DOCTYPE html><p>hello</p>) window dom.windowfunction customDecrypt(n, t) {t t || generateKey(); //…

ZZULIOJ 1108: 打印数字图形(函数专题) (C/C++)

1108: 打印数字图形&#xff08;函数专题&#xff09; 题目描述 从键盘输入一个整数n(1≤n≤9),打印出指定的数字图形。要求在程序中定义并调用如下函数&#xff1a;PrintSpace(m)用来输出m个空格&#xff1b;PrintDigit(m)来输出一行中的数字串"12…m…21"&#xff…

element树形结构下拉组件组装对应格式数据

element树形结构下拉组件组装对应格式数据 <el-row><el-col :span"24"><el-form-item label"购买渠道" prop"treeData" class"grid-content bg-purple"><el-cascaderv-model"testForm.treeData":optio…

鳄鱼指标的3颜色线都代表什么?澳福官网一段话明白了

投资者一直在使用鳄鱼指标进行交易&#xff0c;但是对指标上面的3种颜色的K线都代表什么不明白&#xff1f;直到看到澳福官网一段话才明白&#xff0c;原来这么简单&#xff01; 鳄鱼指标&#xff0c;这一工具是由三条移动平均线组合而成。具体来说&#xff0c;蓝线&#xff0…

8-3、T型加减速单片机程序【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;根据前两节内容&#xff0c;已完成所有计算工作&#xff0c;本节内容介绍具体单片机程序流程及代码 一、程序流程图 根据前两节文章内容可知&#xff0c;T型加减速的关键内容是运动类型的判断以及定时器初值的计算&#xff0c;在输出运动参数后即可判断出运动…

前端代码优化小技巧

导读 今天分享一下开测前端代码的一些优化&#xff0c;及使用的一些小技巧&#xff0c;来优化我们的网站&#xff0c;前端开发中最常见的问题就是很少使用ES6方法导致代码冗余&#xff0c;不够清晰&#xff0c;定时器和闭包导致内存溢出及泄露,网站中css导致排版错乱&#xff…

C++基础——对于C语言缺点的补充(2)

上篇文章中说到&#xff0c;为了解决C语言会出现人为定义的函数和库函数出现重定义的错误&#xff0c;C引入了一个新的概念&#xff0c;即命名空间&#xff0c;通过认为定义命名空间&#xff0c;来解决上述问题。 在本篇文章中&#xff0c;将继续介绍C相对于C语言不足来进行的补…

linux之信号

Linux之信号 什么是信号信号的产生方式signalsignactionkill信号集信号屏蔽 什么是信号 信号机制是一种使用信号来进行进程之间传递消息的方法&#xff0c;信号的全称为软中断信号&#xff0c;简称软中断。 信号的本质是软件层次上对中断的一种模拟&#xff08;软中断&#xff…

Hello Qt!

目录 1. 什么是Qt 2. Qt中的模块 3. 下载安装 4. QtCreator 4. Hello Qt 解释 .pro 解释 main.cpp 解释 mainwindow.ui 解释 mainwindow.h 解释 mainwindow.cpp 5. Qt 中的窗口类 5.1 基础窗口类 5.2 窗口的显示 6. Qt 的坐标体系 7. 内存回收 1. 什么是Qt 是一…

✔ ★【备战实习(面经+项目+算法)】 11.6 学习

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…

【斗破年番】萧炎给彩鳞承诺遭删,熏儿限时返场,古河沦为打工人

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料&#xff0c;在斗破年番69话的最新剧情中&#xff0c;一场感人至深的情感戏份被删减了。在原著中&#xff0c;萧炎曾向美杜莎承诺&#xff0c;他会集齐材料&#xff0c;为她炼制出天雁九行翼。然而&a…

简单2招GET模型参数量计算和输入尺寸随卷积大小变化推导

本文将介绍两种简单且实用的方法&#xff0c;用于计算深度学习模型的参数量&#xff0c;并推导了输入尺寸随卷积大小的变化过程。这些方法可以帮助读者更好地理解模型的复杂度和输入尺寸的变化&#xff0c;为模型设计和优化提供指导。 比如论文中&#xff0c;通常会比较几种模…

机器学习概论

一、机器学习概述 1、机器学习与人工智能、深度学习的关系 人工智能&#xff1a;机器展现的人类智能机器学习&#xff1a;计算机利用已有的数据(经验)&#xff0c;得出了某种模型&#xff0c;并利用此模型预测未来的一种方法。深度学习&#xff1a;实现机器学习的一种技术 2…

龙迅LT6911UXC替换LT6911UXE 支持单PORT 4K60HZ 带HDCP

LT6911UXC 描述&#xff1a; 龙迅LT6911UXC是一个高性能的HDMI2.0到MIPI DSI/CSI & LVDS转换器。HDMI2.0输入支持高达6Gbps的数据速率&#xff0c;这为60Hz的视频提供了足够的带宽。同时&#xff0c;还支持使用HDCP2.2来进行数据解密。对于MIPI DSI/CSI输出&#xff0c;LT…

【二进制转换和与其有关的操作符详解】

文章目录 1.二进制与进制转换2. 2进制转8、10、16进制2.1 2进制转10进制2.2 2进制转8进制2.3 2进制转16进制 3. 8、10、16进制转2进制3.1 10进制转2进制3.2 8进制转2进制3.3 16进制转2进制 4.原码、反码、补码5.移位操作符&#xff08;<< >>&#xff09;5.1左移操作…

英飞凌TLF35584规格书中文

官网&#xff1a; 英飞凌TLF35584QVVS2 TLF35584_SPI&#xff1a; 1 Overview2 Block Diagram3 Pin Configuration3.1 Pin Assignment - PG-VQFN-48 4 General Product Characteristics4.1 Absolute Maximum Ratings 绝对最大额定值4.2 Functional Range4.3 Thermal Resistance…

制造业企业设备管理常见的三个问题及对应的解决方案

当今的市场如同茫茫大海&#xff0c;既充满机遇&#xff0c;也伴随着波动的风险。在现代制造业中&#xff0c;企业常常面临着各种挑战&#xff0c;这些挑战可能妨碍其发展和竞争力。但制造企业往往具备能够解决挑战的能力&#xff0c;借助软件工具的力量&#xff0c;可以更好地…