0. 数据源
/*
Navicat Premium Data Transfer
Source Server : localhost_3306
Source Server Type : MySQL
Source Server Version : 80016
Source Host : localhost:3306
Source Schema : tempdb
Target Server Type : MySQL
Target Server Version : 80016
File Encoding : 65001
Date: 08/05/2023 00:34:36
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for class
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
`id` int(11) NOT NULL,
`c_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES (1, '高三 1 班');
INSERT INTO `class` VALUES (2, '高三 2 班');
INSERT INTO `class` VALUES (3, '高三 3 班');
INSERT INTO `class` VALUES (4, '高三 4 班');
INSERT INTO `class` VALUES (5, '高三 5 班');
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`num` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`age` int(11) NULL DEFAULT NULL,
`tel` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`c_id` int(11) NULL DEFAULT NULL,
`g_id` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`num`) USING BTREE,
INDEX `f_std_cls`(`c_id`) USING BTREE,
CONSTRAINT `f_std_cls` FOREIGN KEY (`c_id`) REFERENCES `class` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (101, '张三', 23, '354456354', 1, NULL);
INSERT INTO `student` VALUES (102, '王五', 34, '4334', 1, 101);
INSERT INTO `student` VALUES (103, '李四', 32, '131434', 3, 101);
INSERT INTO `student` VALUES (104, '赵无极', 23, '4234', 2, NULL);
INSERT INTO `student` VALUES (105, '韩夫子', 34, '23', 2, 102);
INSERT INTO `student` VALUES (106, '高俅', 25, '42543', 4, 103);
INSERT INTO `student` VALUES (107, '范瑶', 26, '2345', NULL, 104);
INSERT INTO `student` VALUES (108, '斯巴达克斯', 27, '422156 ', NULL, 104);
SET FOREIGN_KEY_CHECKS = 1;
1. 多表查询概述
(1)
定义:指从多张表中查询数据。
(2)
笛卡尔积:在数学中,两个集合A和集合B的所有组合情况。在多表查询时,要消除笛卡尔积现象。
// 此时已经产生了笛卡尔现象, 一共返回了40条记录
mysql> select * from student, class;
+-----+------------+------+-----------+------+------+----+-----------+
| num | name | age | tel | c_id | g_id | id | c_name |
+-----+------------+------+-----------+------+------+----+-----------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 1 | 高三 1 班 |
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 2 | 高三 2 班 |
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 3 | 高三 3 班 |
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 4 | 高三 4 班 |
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 5 | 高三 5 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 1 | 高三 1 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 2 | 高三 2 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 3 | 高三 3 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 4 | 高三 4 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 5 | 高三 5 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 1 | 高三 1 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 2 | 高三 2 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 3 | 高三 3 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 4 | 高三 4 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 5 | 高三 5 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 1 | 高三 1 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 2 | 高三 2 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 3 | 高三 3 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 4 | 高三 4 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 5 | 高三 5 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 1 | 高三 1 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 2 | 高三 2 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 3 | 高三 3 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 4 | 高三 4 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 5 | 高三 5 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 1 | 高三 1 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 2 | 高三 2 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 3 | 高三 3 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 4 | 高三 4 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 5 | 高三 5 班 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 | 1 | 高三 1 班 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 | 2 | 高三 2 班 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 | 3 | 高三 3 班 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 | 4 | 高三 4 班 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 | 5 | 高三 5 班 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 | 1 | 高三 1 班 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 | 2 | 高三 2 班 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 | 3 | 高三 3 班 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 | 4 | 高三 4 班 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 | 5 | 高三 5 班 |
+-----+------------+------+-----------+------+------+----+-----------+
(3)
消除笛卡尔现象
多表查询时,指定查询的匹配条件即可过滤掉多余的无效信息。
此处添加查询条件:where student.c_id=class.id
,即可消除笛卡尔现象。
mysql> select * from student, class where student.c_id=class.id;
+-----+--------+------+-----------+------+------+----+-----------+
| num | name | age | tel | c_id | g_id | id | c_name |
+-----+--------+------+-----------+------+------+----+-----------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL | 1 | 高三 1 班 |
| 102 | 王五 | 34 | 4334 | 1 | 101 | 1 | 高三 1 班 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL | 2 | 高三 2 班 |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 | 2 | 高三 2 班 |
| 103 | 李四 | 32 | 131434 | 3 | 101 | 3 | 高三 3 班 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 | 4 | 高三 4 班 |
+-----+--------+------+-----------+------+------+----+-----------+
6 rows in set (0.00 sec)
2. 内连接
在内连接查询中,只有满足条件的记录才能出现在结果关系中。
内连接查询的是两张表的交集部分。
// demo
// 查询每一个学生编号,姓名及其关联的班级名称;
// 下面分别用隐式内连接和显示内连接来实现;
2.1
隐式内连接
格式:SELECT 字段列表 FROM 表1, 表2 WHERE 条件...;
mysql> select student.num,student.name,class.c_name from student, class where student.c_id=class.id;
+-----+--------+-----------+
| num | name | c_name |
+-----+--------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 103 | 李四 | 高三 3 班 |
| 106 | 高俅 | 高三 4 班 |
+-----+--------+-----------+
6 rows in set (0.00 sec)
2.2
显示内连接
格式:SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 条件...;
关键字 INNER 可以省略不写
。
mysql> select student.num,student.name,class.c_name from student INNER JOIN class on student.c_id=class.id;
+-----+--------+-----------+
| num | name | c_name |
+-----+--------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 103 | 李四 | 高三 3 班 |
| 106 | 高俅 | 高三 4 班 |
+-----+--------+-----------+
6 rows in set (0.00 sec)
2.3
结果分析
从结果可以看出,两种方式都产生一样的结果。原本一共有8个学生,内连接查询后只是返回了其中 6个学生。很明显,因为107,108两个学生的c_id为null,不满足where条件,所以没有被查询出来,这也印证了内连接只会将满足条件的记录查询出来或返回两个表的交集部分这一说法。
3. 外连接
3.1
左外连接
返回包括左表中的所有记录和右表中连接字段相等的记录
。
格式:SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件...;
关键字 OUTER 可以省略不写
。
实际上,正式开发中通常都是直接省略了OUTER,直接LEFT JOIN或RIGHT JOIN。也习惯直接称其为左连接或右连接
。
(1)
引子
通过2.3分析可知,内连接只是返回了2个表的交集部分。那假如我不但要获取两个表的交集部分,还要得到107,108两个学生的记录呢?该如何操作???
此时可以通过左连接办到。
(2)
测试
mysql> select student.num,student.name,class.c_name from student LEFT JOIN class on student.c_id=class.id;
+-----+------------+-----------+
| num | name | c_name |
+-----+------------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 103 | 李四 | 高三 3 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 106 | 高俅 | 高三 4 班 |
| 107 | 范瑶 | NULL |
| 108 | 斯巴达克斯 | NULL |
+-----+------------+-----------+
8 rows in set (0.00 sec)
(3)
结论
因为左表中的107、108在右表中没有匹配行,所以返回的右表中的指定列都是null值。
左连接的结果包括指定的左表中的所有行,而不仅仅是连接列所匹配的行。
如果左表的某行在右表中没有匹配行,则在相关联的结果行中,右表的所有选择列均为null值
。
3.2
右外连接
返回包括右表中的所有记录和左表中连接字段相等的记录
。
格式:SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件...;
关键字 OUTER 可以省略不写
。
右连接是左连接的反向状态,将返回右表的所有行。如果右表的某行在左表中没有匹配行,左表将返回null值。
mysql> select student.num,student.name,class.c_name from student RIGHT JOIN class on student.c_id=class.id;
+------+--------+-----------+
| num | name | c_name |
+------+--------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 103 | 李四 | 高三 3 班 |
| 106 | 高俅 | 高三 4 班 |
| NULL | NULL | 高三 5 班 |
+------+--------+-----------+
7 rows in set (0.00 sec)
4. 自连接
在一个连接查询中,涉及的两个表都是同一张表,那么这种查询称之为自连接查询。
语法:SELECT 字段列表 FROM 表A 别名A JOIN 表B 别名B ON 条件...
自连接查询,可以是内连接查询,也可以是外连接查询
。
首先明确g_id字段的含义:
student中的g_id表示组长的意思,如果其值为null,表示对应的学生没有组长,否则就是该学生的组长。比如101,104的g_id为null,表示其没有组长。102,103的g_id都是101,表示102,103学生的组长都是101。
4.1
查询学生 及其 所属组长的名字
// 隐式内连接
mysql> select s1.name as 学生,s2.name as 所属组长 from student s1,student s2 where s1.g_id=s2.num;
+------------+----------+
| 学生 | 所属组长 |
+------------+----------+
| 王五 | 张三 |
| 李四 | 张三 |
| 韩夫子 | 王五 |
| 高俅 | 李四 |
| 范瑶 | 赵无极 |
| 斯巴达克斯 | 赵无极 |
+------------+----------+
6 rows in set (0.00 sec)
// 显式内连接
mysql> select s1.name as 学生,s2.name as 所属组长 from student s1 INNER JOIN student s2 on s1.g_id=s2.num;
+------------+----------+
| 学生 | 所属组长 |
+------------+----------+
| 王五 | 张三 |
| 李四 | 张三 |
| 韩夫子 | 王五 |
| 高俅 | 李四 |
| 范瑶 | 赵无极 |
| 斯巴达克斯 | 赵无极 |
+------------+----------+
6 rows in set (0.00 sec)
4.2
查询所有学生 及其 所属组长的名字,如果学生没有组长,也要查询出来
mysql> select s1.name as 学生,s2.name as 所属组长 from student s1 LEFT JOIN student s2 on s1.g_id=s2.num;
+------------+----------+
| 学生 | 所属组长 |
+------------+----------+
| 张三 | NULL |
| 王五 | 张三 |
| 李四 | 张三 |
| 赵无极 | NULL |
| 韩夫子 | 王五 |
| 高俅 | 李四 |
| 范瑶 | 赵无极 |
| 斯巴达克斯 | 赵无极 |
+------------+----------+
8 rows in set (0.00 sec)
5. 复合条件连接查询
复合条件连接查询是在连接查询的过程中,通过添加过滤条件,以此来限制查询的结果
,使查询的结果更加准确。
// 以下使用了内连接
mysql> select student.num,student.name,class.c_name from student INNER JOIN class on student.c_id=class.id;
+-----+--------+-----------+
| num | name | c_name |
+-----+--------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 103 | 李四 | 高三 3 班 |
| 106 | 高俅 | 高三 4 班 |
+-----+--------+-----------+
6 rows in set (0.00 sec)
// 需求:只返回num>=104的学生;
// 可以添加此条件来再次过滤: AND student.num>=104;
mysql> select student.num,student.name,class.c_name from student INNER JOIN class on student.c_id=class.id AND student.num>=104;
+-----+--------+-----------+
| num | name | c_name |
+-----+--------+-----------+
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 106 | 高俅 | 高三 4 班 |
+-----+--------+-----------+
3 rows in set (0.00 sec)
6. 表的别名机制
select student.num,student.name,class.c_name from student INNER JOIN class on student.c_id=class.id;
上述sql语句中, 多次使用了表名, 假如表名很长, 那这样的话就不利于sql语句的编写, 所以有比要为表名起一个别名来简化sql语句的编写。
格式:表名 [AS] 别名
注意:AS关键字可以省略
。
mysql> select s.num,s.name,c.c_name from student AS s INNER JOIN class AS c on s.c_id=c.id;
+-----+--------+-----------+
| num | name | c_name |
+-----+--------+-----------+
| 101 | 张三 | 高三 1 班 |
| 102 | 王五 | 高三 1 班 |
| 104 | 赵无极 | 高三 2 班 |
| 105 | 韩夫子 | 高三 2 班 |
| 103 | 李四 | 高三 3 班 |
| 106 | 高俅 | 高三 4 班 |
+-----+--------+-----------+
6 rows in set (0.00 sec)
需要注意的是:一旦起了别名,那么整条sql语句中就只能使用别名了,不能再使用表的原名了。否则报错。
// 已经为student表起了别名s, 但是student.c_id处却仍然使用了表名student, 所以报错。
mysql> select s.num,s.name,c.c_name from student AS s INNER JOIN class AS c on student.c_id=c.id;
ERROR 1054 (42S22): Unknown column 'student.c_id' in 'on clause'
7. 表的联合查询union
联合查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
格式:
SELECT 字段列表 FROM 表 A...
UNION [ALL]
SELECT 字段列表 FROM 表B...
;
// 查询num<=104的学生;
mysql> select * from student where num <= 104;
+-----+--------+------+-----------+------+------+
| num | name | age | tel | c_id | g_id |
+-----+--------+------+-----------+------+------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL |
| 102 | 王五 | 34 | 4334 | 1 | 101 |
| 103 | 李四 | 32 | 131434 | 3 | 101 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL |
+-----+--------+------+-----------+------+------+
4 rows in set (0.00 sec)
// 查询age>=23的学生
mysql> select * from student where age >= 23;
+-----+------------+------+-----------+------+------+
| num | name | age | tel | c_id | g_id |
+-----+------------+------+-----------+------+------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL |
| 102 | 王五 | 34 | 4334 | 1 | 101 |
| 103 | 李四 | 32 | 131434 | 3 | 101 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 |
+-----+------------+------+-----------+------+------+
8 rows in set (0.00 sec)
7.1
UNION ALL
// 使用UNION ALL将上述结果直接合并起来
mysql> select * from student where num<=104 UNION ALL select * from student where age>=23;
+-----+------------+------+-----------+------+------+
| num | name | age | tel | c_id | g_id |
+-----+------------+------+-----------+------+------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL |
| 102 | 王五 | 34 | 4334 | 1 | 101 |
| 103 | 李四 | 32 | 131434 | 3 | 101 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL |
| 101 | 张三 | 23 | 354456354 | 1 | NULL |
| 102 | 王五 | 34 | 4334 | 1 | 101 |
| 103 | 李四 | 32 | 131434 | 3 | 101 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 |
+-----+------------+------+-----------+------+------+
12 rows in set (0.00 sec)
7.2
UNION
// 通过UNION ALL只是将结果给合并起来, 里面可能含有重复的;
// 通过UNION可以达到去重的效果;
mysql> select * from student where num<=104 UNION select * from student where age>=23;
+-----+------------+------+-----------+------+------+
| num | name | age | tel | c_id | g_id |
+-----+------------+------+-----------+------+------+
| 101 | 张三 | 23 | 354456354 | 1 | NULL |
| 102 | 王五 | 34 | 4334 | 1 | 101 |
| 103 | 李四 | 32 | 131434 | 3 | 101 |
| 104 | 赵无极 | 23 | 4234 | 2 | NULL |
| 105 | 韩夫子 | 34 | 23 | 2 | 102 |
| 106 | 高俅 | 25 | 42543 | 4 | 103 |
| 107 | 范瑶 | 26 | 2345 | NULL | 104 |
| 108 | 斯巴达克斯 | 27 | 422156 | NULL | 104 |
+-----+------------+------+-----------+------+------+