MySQL多表操作 查询&子查询&外键约束

除了针对一张表的操作,还有对两张以上的表进行的操作。多表操作的核心 是把两张或多张表的数据,通过共同的关联字段,按指定的规则合并成一个完整的结果集。

比如你要查「订单的订单号、下单用户的姓名、订单里的商品名称」,这些数据分别在订单表、用户表、商品表、订单商品表,必须用联合查询才能一次性拿到结果,不用多次单表查询拼接数据。

多表查询

联合查询

联合查询时多表查询的一种,把多个 SELECT 语句的结果集,按行合并成一个结果集

复制代码
# UNION:合并后自动去重
SELECT phone FROM user
UNION
SELECT receiver_phone FROM order;

# UNION ALL:直接合并所有数据,不去重,性能更高
SELECT phone FROM user
UNION ALL
SELECT receiver_phone FROM order;

UNION是实现联合查询的关键字,ALL表示合并所有数据,distinct是默认值表示去除完全重复的记录。

核心规则

  • 列数相同:每个SELECT语句中的列数必须相等。

  • 数据类型兼容:对应位置的列必须拥有相似的数据类型。

  • 顺序一致:每个SELECT语句中的列顺序必须一致

构建商品表与服务表并插入数据

使用UNION联合查询

联合查询的结果在只保留第一个select语句对应的字段名称,MySQL仅会根据查询字段出现的顺序对结果进行合并。

连接查询

在实际应用中,可以根据多表之间的关联关系,将多张数据表连到一起,MySQL中常用的连接查询有交叉连接、内连接、左外连接和右外连接

1. 交叉连接

交叉连接 也称为笛卡尔积 ,它的核心逻辑是将左表的每一行 与右表的每一行 进行组合;如果表 A 有 s 字段 n 行,表 B 有 h 字段 m 行,那么执行交叉连接后的结果集将会有(s+h)字段 n*m 行;通过关键字CROSS JOIN实现

复制代码
select 查询字段 from 表1 cross join 表2;

colors数据表中有三种颜色,sizes数据表中有三种尺码,经过连接查询得到9条数据。

  • 交叉连接不关心两表之间是否有逻辑关联,只是机械地进行穷举组合

  • 在处理大数据量表时要极其小心,因为行数是乘积级增长的。

内连接

内连接是一种非常常见的连接查询,它的核心逻辑是仅返回两个表中满足连接条件的交集部分,如果某行数据在其中一个表中没有匹配的对应项,那么这行数据就不会出现在最终的结果集中。

复制代码
select 查询字段 from 表1
JOIN 表2 ON 匹配条件;

ON用于指定内连接的查询条件,ON与WHERE限定效果一样但ON能节省性能。

在查询数据时,如果两个表中有同名的字段,为了避免重名出现错误,使用**"数据表.字段名"**的方式进行区分。

左外连接

左外连接的核心 是以左表为基准,返回左表的所有行,以及右表中与之匹配的行,如果左表的某行在右表中没有匹配,结果集中该行对应的右表字段将显示为NULL。

复制代码
select 查询字段 from 表1
left join 表2 on 匹配条件;

关键字left join左边的表被称为左表/主表,右边的表被称为右表/从表

想象你在整理一个清单:

  • 左表:你拥有的所有物品。

  • 右表:这些物品的保修信息。

  • 左连接的结果:你会得到你所有的物品清单。有保修的会显示保修期,没保修的则会留空(NULL)

左连接查询中,即使左表中有的数据跟右表中任何数据都不匹配,也会在查询结果保留该数据。

右外连接

右外连接 的逻辑与左连接正好相反:它以右表为基准,返回右表的所有行,以及左表中与之匹配的行。

复制代码
select 查询字段
from 表1 right join 表2 on 匹配条件

外连接与内连接的区别

  • 内连接是只有当两边都有对应数据时才返回数据结果

  • 外连接至少会完整保留其中一张表的所有行

子查询

通俗来讲,子查询就是嵌套在另一个 SQL 查询内部的查询。你可以把它理解为"查询中的查询",这个子查询语句是一条完整、独立的select语句。

在 SQL 执行时,数据库会先运行子查询,将其结果作为主查询的输入条件或数据源。在含有子查询的语句里,子查询必须书写在括号内 ,然后再将返回的结果作为外层SQL语句的过滤条件;如果一个语句里含有多层子查询,执行顺序是从最里层的子查询开始。

按照返回的内容子查询可以分为标量子查询、行/列子查询、表子查询 ,按照子查询的出现位置可以分为where子查询和from子查询

标量子查询

标量子查询的结果只返回"一个单元格"的数据(即单行、单列的一个具体数值,如一个数字、一个字符串或一个日期)

  • 必须放在括号内

  • 结果唯一

    where 条件判断 {=|<>}
    (select 字段名 from 数据源 [where][group by][having][order by][limit])

这里有两个过程:

  • 子查询运行结果: 数据库先算这一步,它只返回一个数字(服务的平均价格)。因为它是"一行一列",所以它是标量的

  • 主查询运行结果: 主查询拿到这个数字后,去执行where price > 判断条件。主查询最后返回多少行、多少列。

如果子查询返回了多行数据,主查询不知道该跟哪一个比,就会发生报错

复制代码
SELECT service_name 
FROM services 
WHERE price > (SELECT price FROM services); 
#报错!子查询返回了多行价格,主查询不知道该跟哪一个比。

列子查询

列子查询 是指子查询的结果返回了一列多行的数据

复制代码
where 条件判断 (in|not in)
(select 字段名 from 数据源 [where][group by][having][order by][limit])

因为它的结果不再是一个单一的数值,而是一个列表 ,所以你不能再使用简单的=或>进行条件判断,需要使用比较函数IN()或者NOT IN()来进行条件判断。

括号里的子查询是一个列子查询,它会返回一串 ID,比如 (1, 1, 2),主查询拿着goods表里面的id一个个匹配是否在列表里面。

行子查询

行子查询是指子查询的结果返回了"一行多列"的数据,行子查询返回一个包含多个属性的元组,它允许你同时比较多个字段的值。

复制代码
where (字段名...) =
(select 字段名... from 数据源 [where][group by][having][order by][limit])

SELECT service_name, item_id, price
FROM services
WHERE (item_id, price) = (
    SELECT item_id, price 
    FROM services 
    WHERE service_name = '深度清理'
    LIMIT 1
);

表子查询

表子查询 返回的结果是一个多行多列的数据集

复制代码
select 字段 from (select语句) as 别名
[where][group by][having][order by][limit];
  • 在from子句中使用表子查询时,MySQL 要求必须给这个子查询的结果起一个别名,否则会报错

  • 用于对原始数据进行"二次加工"。先通过子查询筛选、聚合出一部分数据,主查询再基于这些数据进行进一步操作

复制代码
  SELECT 
      temp.item_id, 
      temp.avg_price
  FROM (
      SELECT item_id, AVG(price) AS avg_price
      FROM services
      GROUP BY item_id
  ) AS temp 
  #必须起别名,这里叫 temp
  WHERE temp.avg_price > 100;

总而言之,子查询本质上就是在select语句的where里面再套一个select语句

子查询关键字

  • IN / NOT IN:判断某个值是否在子查询返回的列表中

  • any:只要主查询的值与子查询返回的任何一个值比较结果为true,条件就成立

  • all:主查询的值必须与子查询返回的所有值比较结果都为true,条件才成立

any和all都必须配合比较操作符(>、=、<)才能使用

1. 带有关键字any的子查询

复制代码
where 表达式 比较运算符 any(子查询语句)

查询价格比任意一个 基础服务(假设 ID 为 1,2,3)贵的服务,只要比这三个里最小的那个大,就会被选中

2. 带有关键字all的子查询

复制代码
SELECT service_name FROM services 
WHERE price > ALL (SELECT price FROM services WHERE item_id = 1);

查询比所有服务平均价格都要高的服务。

外键约束

在数据库设计时,为了保证不同表中相同含义的数据的一致性和完整性 ,可以为数据添加外键约束 。简单来说,外键是一个表中的字段,它的值必须匹配另一个表的主键。它就像一条"逻辑绳索",把两张表牢牢地绑在一起。

  • 只有InnoDB存储引擎支持外键约束

  • 建立外键约束的两个数据表相关字段的数据类型必须相似,也就是要求字段的数据类型可以相互转换

  • 主表则必须是有主键约束或unique约束

为什么需要外键约束?

  • 保证数据完整性:子表里的数据,必须在主表里存在,防止随便创建一个子表数据(脏数据)

  • 防止随意删除主表数据

  • 实现级联操作

添加外键约束

1. 在建表时指定外键

复制代码
CREATE TABLE services (
    id INT AUTO_INCREMENT PRIMARY KEY,
    service_name VARCHAR(255) NOT NULL,
    item_id INT,
    # 定义外键约束
    CONSTRAINT fk_gooos_item 
    FOREIGN KEY (item_id) REFERENCES gooos(id)
) ENGINE=InnoDB;
  • 关键字constraint用于定义外键约束的名称,如果省略,MySQL会自动生成一个名称。

  • 关键词foreign key ...references向数据表添加外键约束

2. 为已有表添加外键

复制代码
ALTER TABLE services
ADD CONSTRAINT fk_gooos_item
FOREIGN KEY (item_id) REFERENCES gooos(id);

删除外键约束

复制代码
ALTER TABLE services DROP FOREIGN KEY fk_gooos_item;

MySQL 在创建外键时,通常会同步创建一个同名的索引(Index)。如果你想彻底清理干净,删除外键后还需要手动删除索引

复制代码
ALTER TABLE services DROP INDEX fk_gooos_item;

使用外键约束也有一定的劣势

  • 使用外键约束会带来额外开销

  • 主表被锁定时,会引发从表也被锁定

  • 删除主表的数据时,需要先删除从表的数据

相关推荐
橙子圆1231 小时前
Redis知识6之事务
数据库·redis·缓存
不会摸鱼的小鱼1 小时前
WSL 安装 Ubuntu 22.04 到指定磁盘
数据库·postgresql·php
m0_702036531 小时前
mysql如何导出特定条件的查询数据_使用mysqldump加where参数
jvm·数据库·python
正在走向自律1 小时前
标量子查询消除:数据库优化器的一场“等价变戏法”
数据库·sql 优化·金仓数据库·数据库性能调优·标量子查询·数据库优化器
逻极1 小时前
SQLite 从入门到精通:深入理解嵌入式数据库的艺术与科学
数据库·sqlite·记忆·sqlite从入门到精通
看到代码头都是大的1 小时前
CentoOS7安装mysql 8.0.46
mysql
未来之窗软件服务1 小时前
数据库优化(九)随机抽选系统数据表 ——东方仙盟
大数据·数据库·数据库优化·仙盟创梦ide·东方仙盟
爱喝水的鱼丶1 小时前
SAP-ABAP:新手入门篇——从0到1写出你的第一个ABAP Hello World程序并完成调试运行
运维·服务器·数据库·学习·sap·abap
m0_733565462 小时前
bootstrap怎么实现响应式的文章瀑布流布局
jvm·数据库·python