MySQL-子查询及连接

该博客记录了MySQL相关知识,包括子查询,即一个查询作为另一个查询的条件,且需用括号括起;ANY SOME ALL关键字用于处理子查询多行结果;还涉及多表更新、连接(内连接、左连接、右连接)、无限级分类和多表删除等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

子查询

当一个查询是另一个查询的条件时,称之为子查询。

  • 子查询必须用括号括起来。
    由比较运算符引发的子查询:
    先新建一个数据表
CREATE TABLE students (
    id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    class_name VARCHAR(20) NOT NULL,
    teacher_name VARCHAR(20) NOT NULL,
    score SMALLINT UNSIGNED
);
-- 插入几条数据
INSERT INTO students VALUES 
(NULL,'张山','网络18-1班','陈老师',78), 
(NULL,'王五','微机18-1班','张老师',85),
(NULL,'赵六','设计18-1班','林老师',66),
(NULL,'陈七','网络18-1班','陈老师',90),
(NULL,'阿狗','电子18-1班','王老师',88),
(NULL,'狗腿','电子18-1班','王老师',95),
(NULL,'陈七','网络18-1班','陈老师',90),
(NULL,'小咪','设计18-1班','林老师',98),
(NULL,'富贵','微机18-1班','张老师',83),
(NULL,'王五','微机18-1班','张老师',85);

现在查出他们的平均成绩

SELECT ROUND(AVG(score),1) FROM students;
+---------------------+
| ROUND(AVG(score),1) |
+---------------------+
|                85.8 |
+---------------------+

查出比平均成绩高的学生

SELECT * FROM students WHERE score > (SELECT ROUND(AVG(score),1) FROM students);
+----+--------+---------------+--------------+-------+
| id | name   | class_name    | teacher_name | score |
+----+--------+---------------+--------------+-------+
|  4 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  5 | 阿狗   | 电子18-1班    | 王老师       |    88 |
|  6 | 狗腿   | 电子18-1班    | 王老师       |    95 |
|  7 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  8 | 小咪   | 设计18-1班    | 林老师       |    98 |
+----+--------+---------------+--------------+-------+
ANY SOME ALL关键字

对子查询中返回的多行结果进行处理

···ANYSOMEALL
>、>=最小值最小值最大值
<、<=最大值最大值最小值
=任意值任意值
<>、!=最大值

使用: 查询

-- 先查询下林老师学生的分数
SELECT score FROM students WHERE teacher_name = '林老师';
+-------+
| score |
+-------+
|    66 |
|    98 |
+-------+
-- 找出比林老师学生分数高的学生
SELECT * FROM students 
WHERE score > (SELECT score FROM students WHERE teacher_name = '林老师');
-- 不使用关键字时会报错
-- ERROR 1242 (21000): Subquery returns more than 1 row
-- 返回结果超过一行,不知道该跟那个做对比了
-- 使用关键字, 图标中可以看到,使用 ‘>’ ANY会跟返回结果中值最小的对比 最小的分数为66
SELECT * FROM students 
WHERE score > ANY (SELECT score FROM students WHERE teacher_name = '林老师');
+----+--------+---------------+--------------+-------+
| id | name   | class_name    | teacher_name | score |
+----+--------+---------------+--------------+-------+
|  1 | 张山   | 网络18-1班    | 陈老师       |    78 |
|  2 | 王五   | 微机18-1班    | 张老师       |    85 |
|  4 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  5 | 阿狗   | 电子18-1班    | 王老师       |    88 |
|  6 | 狗腿   | 电子18-1班    | 王老师       |    95 |
|  7 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  8 | 小咪   | 设计18-1班    | 林老师       |    98 |
|  9 | 富贵   | 微机18-1班    | 张老师       |    83 |
| 10 | 王五   | 微机18-1班    | 张老师       |    85 |
+----+--------+---------------+--------------+-------+
-- 使用 ‘<’ ANY会跟最大的对比,最大分为98
SELECT * FROM students 
WHERE score < ANY (SELECT score FROM students WHERE teacher_name = '林老师');
+----+--------+---------------+--------------+-------+
| id | name   | class_name    | teacher_name | score |
+----+--------+---------------+--------------+-------+
|  1 | 张山   | 网络18-1班    | 陈老师       |    78 |
|  2 | 王五   | 微机18-1班    | 张老师       |    85 |
|  3 | 赵六   | 设计18-1班    | 林老师       |    66 |
|  4 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  5 | 阿狗   | 电子18-1班    | 王老师       |    88 |
|  6 | 狗腿   | 电子18-1班    | 王老师       |    95 |
|  7 | 陈七   | 网络18-1班    | 陈老师       |    90 |
|  9 | 富贵   | 微机18-1班    | 张老师       |    83 |
| 10 | 王五   | 微机18-1班    | 张老师       |    85 |
+----+--------+---------------+--------------+-------+

多表更新

-- 新建班级表
CREATE TABLE classes(
class_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(20) NOT NULL,
teacher_name VARCHAR(20) NOT NULL
);
/
-- 将学生表的班级及老师写入班级表
INSERT classes (class_name,teacher_name)
SELECT class_name,teacher_name 
FROM students GROUP BY class_name;
-- 查询结果
SELECT * FROM classes;
+----------+---------------+--------------+
| class_id | class_name    | teacher_name |
+----------+---------------+--------------+
|        1 | 微机18-1班    | 张老师       |
|        2 | 电子18-1班    | 王老师       |
|        3 | 网络18-1班    | 陈老师       |
|        4 | 设计18-1班    | 林老师       |
+----------+---------------+--------------+
-- 正文
-- 将学生表班级名更改成班级id
UPDATE students INNER JOIN (SELECT class_id,class_name FROM classes ) AS cl
ON students.class_name = cl.class_name SET students.class_name = cl.class_id; 
-- 查看结果
+----+--------+------------+--------------+-------+
| id | name   | class_name | teacher_name | score |
+----+--------+------------+--------------+-------+
|  1 | 张山   | 1          | 陈老师       |    78 |
|  2 | 王五   | 2          | 张老师       |    85 |
|  3 | 赵六   | 3          | 林老师       |    66 |
|  4 | 陈七   | 1          | 陈老师       |    90 |
|  5 | 阿狗   | 4          | 王老师       |    88 |
|  6 | 狗腿   | 4          | 王老师       |    95 |
|  7 | 陈七   | 1          | 陈老师       |    90 |
|  8 | 小咪   | 3          | 林老师       |    98 |
|  9 | 富贵   | 2          | 张老师       |    83 |
| 10 | 王五   | 2          | 张老师       |    85 |
+----+--------+------------+--------------+-------+
-- 另一种方法
-- 建表时直接写入
CREATE TABLE teachers (
teacher_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
teacher_name VARCHAR(20) NOT NULL
)
SELECT teacher_name  FROM students GROUP BY teacher_name;
-- 然后更新
UPDATE students INNER JOIN (SELECT * FROM  teachers) 
AS  t ON students.teacher_name = t.teacher_name 
SET students.teacher_name = t.teacher_id;
-- 最后更改下学生表字段名
ALTER TABLE students CHANGE class_name class_id SMALLINT 
UNSIGNED  NOT NULL;
ALTER TABLE students CHANGE teacher_name teacher_id SMALLINT UNSIGNED NOT NULL;

连接

内连接的连接查询结果集中仅包含满足条件的行,外连接(左、右)的连接查询结果集中既包含那些满足条件的行,还包含其中某个表的全部行

1、内连接
-- 写入两条数据
INSERT INTO students VALUES (NULL,'张伟','10','12',100),
(NULL,'刘六','12','10',100);
INSERT INTO classes VALUE (NULL,'机电18-1班','潘老师');
-- 连接查询
-- 显示班级
SELECT id,name,cl.class_name FROM students 
INNER JOIN classes AS cl ON students.class_id = cl.class_id;
+----+--------+---------------+
| id | name   | class_name    |
+----+--------+---------------+
|  1 | 张山   | 网络18-1班    |
|  2 | 王五   | 微机18-1班    |
|  3 | 赵六   | 设计18-1班    |
|  4 | 陈七   | 网络18-1班    |
|  5 | 阿狗   | 电子18-1班    |
|  6 | 狗腿   | 电子18-1班    |
|  7 | 陈七   | 网络18-1班    |
|  8 | 小咪   | 设计18-1班    |
|  9 | 富贵   | 微机18-1班    |
| 10 | 王五   | 微机18-1班    |
+----+--------+---------------+
-- 里面没有张伟跟刘六,是因为他们不符合条件 
-- 张伟和刘六的class_id 为10,12
+----------+---------------+--------------+
| class_id | class_name    | teacher_name |
+----------+---------------+--------------+
|        1 | 微机18-1班    | 张老师       |
|        2 | 电子18-1班    | 王老师       |
|        3 | 网络18-1班    | 陈老师       |
|        4 | 设计18-1班    | 林老师       |
|        5 | 机电18-1班    | 潘老师       |
+----------+---------------+--------------+
-- 表里是没有这两个class_id的,所以不会显示
-- 同理 没有学生的class_id为5,所以也不符合条件
2、左连接
-- 还是查询班级
SELECT id,name,cl.class_name FROM students 
LEFT JOIN classes AS cl ON students.class_id = cl.class_id;
+----+--------+---------------+
| id | name   | class_name    |
+----+--------+---------------+
|  2 | 王五   | 微机18-1班    |
|  9 | 富贵   | 微机18-1班    |
| 10 | 王五   | 微机18-1班    |
|  5 | 阿狗   | 电子18-1班    |
|  6 | 狗腿   | 电子18-1班    |
|  1 | 张山   | 网络18-1班    |
|  4 | 陈七   | 网络18-1班    |
|  7 | 陈七   | 网络18-1班    |
|  3 | 赵六   | 设计18-1班    |
|  8 | 小咪   | 设计18-1班    |
| 11 | 张伟   | NULL          |
| 12 | 刘六   | NULL          |
+----+--------+---------------+
-- 左连接说明JOIN左侧的所有记录均会被显示,无论其在右侧是否得到匹配
-- 刘六 张伟 没有班级,会显示为NULL
3、右连接
SELECT id,name,cl.class_name FROM students 
RIGHT JOIN classes AS cl ON students.class_id = cl.class_id;
+------+--------+---------------+
| id   | name   | class_name    |
+------+--------+---------------+
|    1 | 张山   | 网络18-1班    |
|    2 | 王五   | 微机18-1班    |
|    3 | 赵六   | 设计18-1班    |
|    4 | 陈七   | 网络18-1班    |
|    5 | 阿狗   | 电子18-1班    |
|    6 | 狗腿   | 电子18-1班    |
|    7 | 陈七   | 网络18-1班    |
|    8 | 小咪   | 设计18-1班    |
|    9 | 富贵   | 微机18-1班    |
|   10 | 王五   | 微机18-1班    |
| NULL | NULL   | 机电18-1班    |
+------+--------+---------------+

-- 右连接说明JOIN右侧的所有记录均会被显示,无论其在左侧是否得到匹配
-- 机电18-1班没有学生,会显示为NULL

无限级分类

-- 新建省份城市表
CREATE TABLE areas (
id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
area_name VARCHAR(30) NOT NULL,
parent_id SMALLINT UNSIGNED NOT NULL
);
-- 写入一些数据
INSERT INTO areas VALUES 
(NULL,'广东省',0),
(NULL,'江苏省',0),
(NULL,'山东省',0),
(NULL,'浙江省',0),
(NULL,'河南省',0),
(NULL,'广州市',1),
(NULL,'深圳市',1),
(NULL,'南京市',2),
(NULL,'济南市',3),
(NULL,'青岛市',3),
(NULL,'杭州市',4),
(NULL,'宁波市',4),
(NULL,'郑州市',5);

+----+-----------+-----------+
| id | area_name | parent_id |
+----+-----------+-----------+
|  1 | 广东省    |         0 |
|  2 | 江苏省    |         0 |
|  3 | 山东省    |         0 |
|  4 | 浙江省    |         0 |
|  5 | 河南省    |         0 |
|  6 | 广州市    |         1 |
|  7 | 深圳市    |         1 |
|  8 | 南京市    |         2 |
|  9 | 济南市    |         3 |
| 10 | 青岛市    |         3 |
| 11 | 杭州市    |         4 |
| 12 | 宁波市    |         4 |
| 13 | 郑州市    |         5 |
+----+-----------+-----------+
-- 查询城市所属省
SELECT a.id,a.area_name,p.area_name AS city 
FROM areas AS a LEFT JOIN (SELECT * FROM areas ) AS p ON a.id = p.parent_id;
+----+-----------+-----------+
| id | area_name | city      |
+----+-----------+-----------+
|  1 | 广东省    | 广州市    |
|  1 | 广东省    | 深圳市    |
|  2 | 江苏省    | 南京市    |
|  3 | 山东省    | 济南市    |
|  3 | 山东省    | 青岛市    |
|  4 | 浙江省    | 杭州市    |
|  4 | 浙江省    | 宁波市    |
|  5 | 河南省    | 郑州市    |
|  6 | 广州市    | NULL      |
|  7 | 深圳市    | NULL      |
|  8 | 南京市    | NULL      |
|  9 | 济南市    | NULL      |
| 10 | 青岛市    | NULL      |
| 11 | 杭州市    | NULL      |
| 12 | 宁波市    | NULL      |
| 13 | 郑州市    | NULL      |
+----+-----------+-----------+
-- 内连接
SELECT a.id,a.area_name,p.area_name AS city 
FROM areas AS a INNER JOIN (SELECT * FROM areas ) AS p ON a.id = p.parent_id;
+----+-----------+-----------+
| id | area_name | city      |
+----+-----------+-----------+
|  1 | 广东省    | 广州市    |
|  1 | 广东省    | 深圳市    |
|  2 | 江苏省    | 南京市    |
|  3 | 山东省    | 济南市    |
|  3 | 山东省    | 青岛市    |
|  4 | 浙江省    | 杭州市    |
|  4 | 浙江省    | 宁波市    |
|  5 | 河南省    | 郑州市    |
+----+-----------+-----------+

多表删除

-- 由于只有一张表,就用这一张演示,多表同理
-- 查看students
+----+--------+----------+------------+-------+
| id | name   | class_id | teacher_id | score |
+----+--------+----------+------------+-------+
|  1 | 张山   |        3 |          4 |    78 |
|  2 | 王五   |        1 |          1 |    85 |
|  3 | 赵六   |        4 |          2 |    66 |
|  4 | 陈七   |        3 |          4 |    90 |
|  5 | 阿狗   |        2 |          3 |    88 |
|  6 | 狗腿   |        2 |          3 |    95 |
|  7 | 陈七   |        3 |          4 |    90 |
|  8 | 小咪   |        4 |          2 |    98 |
|  9 | 富贵   |        1 |          1 |    83 |
| 10 | 王五   |        1 |          1 |    85 |
| 11 | 张伟   |       10 |         12 |   100 |
| 12 | 刘六   |       12 |         10 |   100 |
+----+--------+----------+------------+-------+
-- 会发现有几个重复的
-- 查看重复的用户
SELECT * FROM students GROUP BY name HAVING COUNT(name) > 1;
+----+--------+----------+------------+-------+
| id | name   | class_id | teacher_id | score |
+----+--------+----------+------------+-------+
|  2 | 王五   |        1 |          1 |    85 |
|  4 | 陈七   |        3 |          4 |    90 |
+----+--------+----------+------------+-------+
-- 接下来把存入时间晚的重复学生删除
DELETE t1 FROM students AS t1 
LEFT JOIN (SELECT * FROM students GROUP BY name HAVING COUNT(name)>1) AS t2 
ON t1.name = t2.name WHERE t1.id > t2.id;
+----+--------+----------+------------+-------+
| id | name   | class_id | teacher_id | score |
+----+--------+----------+------------+-------+
|  1 | 张山   |        3 |          4 |    78 |
|  2 | 王五   |        1 |          1 |    85 |
|  3 | 赵六   |        4 |          2 |    66 |
|  4 | 陈七   |        3 |          4 |    90 |
|  5 | 阿狗   |        2 |          3 |    88 |
|  6 | 狗腿   |        2 |          3 |    95 |
|  8 | 小咪   |        4 |          2 |    98 |
|  9 | 富贵   |        1 |          1 |    83 |
| 11 | 张伟   |       10 |         12 |   100 |
| 12 | 刘六   |       12 |         10 |   100 |
+----+--------+----------+------------+-------+
-- ?

为自己学习记录,错误之处请帮助指出,共同学习,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值