一、测试表:
1. 学生表
CREATE TABLE `students` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(12) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`gender` tinyint(1) DEFAULT NULL,
`age` tinyint DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2. 课程表
CREATE TABLE `subject` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '科目名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3. 学生成绩表
CREATE TABLE `score` (
`id` int NOT NULL AUTO_INCREMENT,
`subject_id` int DEFAULT NULL,
`score` smallint DEFAULT NULL,
`student_id` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4. 学生成绩等级表:
create table grade(
id int primary key auto_increment,
min_score tinyint(3),
max_score tinyint(3),
name char(1)
)engine=innodb default charset=utf8mb4 collate=utf8mb4_unicode_ci;
说明:表结构只为满足实验要求,没有实际业务逻辑参考价值。
二、笛卡尔乘积现象:
mysql> select * from subject su,score sc;
| id | name | id | subject_id | score | student_id |
+----+--------+----+------------+-------+------------+
| 5 | 物理 | 1 | 1 | 80 | 1 |
| 4 | 化学 | 1 | 1 | 80 | 1 |
| 3 | 英语 | 1 | 1 | 80 | 1 |
| 2 | 数学 | 1 | 1 | 80 | 1 |
| 1 | 语文 | 1 | 1 | 80 | 1 |
| 5 | 物理 | 2 | 2 | 79 | 1 |
| 4 | 化学 | 2 | 2 | 79 | 1 |
| 3 | 英语 | 2 | 2 | 79 | 1 |
| 2 | 数学 | 2 | 2 | 79 | 1 |
| 1 | 语文 | 2 | 2 | 79 | 1 |
| 5 | 物理 | 3 | 3 | NULL | 1 |
| 4 | 化学 | 3 | 3 | NULL | 1 |
| 3 | 英语 | 3 | 3 | NULL | 1 |
| 2 | 数学 | 3 | 3 | NULL | 1 |
| 1 | 语文 | 3 | 3 | NULL | 1 |
如上查询所示:当同时查询多表时,多表之间没有有效的的连接条件时,每张表的每行数据都将与另外几张表的每行数据进行关联输出,导致查询结果错误。
若表1中有5条数据,表2中有20条数据,在同时查询两张表时,结果将会有 5*20=100条数据
为避免笛卡尔乘积现象的发生,则需要在多表查询时添加有效的连接条件。
如:
mysql> select * from subject su,score sc where su.id=sc.subject_id;
+----+--------+----+------------+-------+------------+
| id | name | id | subject_id | score | student_id |
+----+--------+----+------------+-------+------------+
| 1 | 语文 | 1 | 1 | 80 | 1 |
| 2 | 数学 | 2 | 2 | 79 | 1 |
| 3 | 英语 | 3 | 3 | NULL | 1 |
| 1 | 语文 | 6 | 1 | 86 | 2 |
| 2 | 数学 | 7 | 2 | 97 | 2 |
| 3 | 英语 | 8 | 3 | 85 | 2 |
| 1 | 语文 | 11 | 1 | 90 | 3 |
| 2 | 数学 | 12 | 2 | NULL | 3 |
| 3 | 英语 | 13 | 3 | NULL | 3 |
| 1 | 语文 | 16 | 1 | NULL | 4 |
| 2 | 数学 | 17 | 2 | 87 | 4 |
| 3 | 英语 | 18 | 3 | 45 | 4 |
+----+--------+----+------------+-------+------------+
12 rows in set (0.00 sec)
三、JOIN连接图
网上找了一张各种JOIN连接查询结果图:
四、内连接
内连接有两种语法标准:SQL92标准和SQL99标准
SQL92标准语法:
select 查询列表 from table_name1,table_name2,... where 连接条件 order by 排序规则
SQL99标准语法:
select 查询列表 from table_name1 [inner] join table_name2 on 连接条件 [where 查询条件 group by 分组 having 筛选条件 order by 排序规则 limit 分页查询]
1)等值连接
#1. 查询学生的各科成绩
mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st,subject su,score sc where st.id=sc.student_id and sc.subject_id=su.id;
mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on sc.subject_id=su.id;
+-----------+--------+-------+
| 姓名 | 科目 | 分数 |
+-----------+--------+-------+
| 张三 | 语文 | 80 |
| 张三 | 数学 | 79 |
| 张三 | 英语 | NULL |
| 李四 | 语文 | 86 |
| 李四 | 数学 | 97 |
| 李四 | 英语 | 85 |
| 周芷若 | 语文 | 90 |
| 周芷若 | 数学 | NULL |
| 周芷若 | 英语 | NULL |
| 赵敏 | 语文 | NULL |
| 赵敏 | 数学 | 87 |
| 赵敏 | 英语 | 45 |
+-----------+--------+--------+
12 rows in set (0.00 sec)
#2. 查询有成绩的学生的各科成绩
mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st,subject su,score sc where st.id=sc.student_id and sc.subject_id=su.id and sc.score is not null;
mysql> select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on sc.subject_id=su.id where sc.score is not null;
+-----------+--------+--------+
| 姓名 | 科目 | 分数 |
+-----------+--------+--------+
| 张三 | 语文 | 80 |
| 张三 | 数学 | 79 |
| 李四 | 语文 | 86 |
| 李四 | 数学 | 97 |
| 李四 | 英语 | 85 |
| 周芷若 | 语文 | 90 |
| 赵敏 | 数学 | 87 |
| 赵敏 | 英语 | 45 |
+-----------+--------+--------+
8 rows in set (0.00 sec)
#3. 查询每个学生的总成绩,并按总成绩倒序排列
mysql> select st.name 姓名,sum(sc.score) 总成绩 from students st,score sc where st.id=sc.student_id group by st.name order by 总成绩 desc;
mysql> select st.name 姓名,sum(sc.score) 总成绩 from students st join score sc on st.id=sc.student_id group by st.name order by 总成绩 desc;
+-----------+-----------+
| 姓名 | 总成绩 |
+-----------+-----------+
| 李四 | 325 |
| 张三 | 318 |
| 赵敏 | 200 |
| 周芷若 | 150 |
+-----------+-----------+
4 rows in set (0.00 sec)
2)非等值连接
#1. 查询出学生的各科成绩及对应的级别
mysql> select a.*,b.name 级别 from (select st.name 姓名,su.name 科目,sc.score 分数 from students st inner join score sc on st.id=sc.student_id join subject su on sc.subject_id=su.id where sc.score is not null) a join grade b on a.分数 between b.min_score and b.max_score;
+-----------+--------+--------+--------+
| 姓名 | 科目 | 分数 | 级别 |
+-----------+--------+--------+--------+
| 赵敏 | 英语 | 45 | A级 |
| 张三 | 数学 | 79 | B级 |
| 赵敏 | 数学 | 87 | C级 |
| 赵敏 | 语文 | 86 | C级 |
| 李四 | 英语 | 85 | C级 |
| 李四 | 语文 | 86 | C级 |
| 张三 | 语文 | 80 | C级 |
| 周芷若 | 语文 | 90 | D级 |
| 李四 | 数学 | 97 | D级 |
+-----------+--------+--------+--------+
9 rows in set (0.05 sec)
3)自连接
#1. 查询出各科成绩前2名的学生姓名,科目,及分数
select
a.*
from
(
select
st.name 姓名,su.name 科目,sc.score 成绩
from
students st,subject su,score sc
where
st.id=sc.student_id and su.id=sc.subject_id
) a
where
2 > (
select
count(*)
from
(
select
st.name 姓名,su.name 科目,sc.score 成绩
from
students st,subject su,score sc
where
st.id=sc.student_id and su.id=sc.subject_id
) b
where
b.科目=a.科目 and b.成绩>a.成绩
)
and
a.成绩 is not null
order by
a.科目, a.成绩 desc;
+-----------+--------+--------+
| 姓名 | 科目 | 成绩 |
+-----------+--------+--------+
| 李四 | 数学 | 97 |
| 赵敏 | 数学 | 87 |
| 李四 | 英语 | 85 |
| 赵敏 | 英语 | 45 |
| 周芷若 | 语文 | 90 |
| 李四 | 语文 | 86 |
| 赵敏 | 语文 | 86 |
+-----------+--------+--------+
7 rows in set (0.00 sec)
二、外连接:
1)左外连接:
select 查询列表 from table_name1 left [outer] join table_name2 on 连接条件 [where 查询条件 group by 分组列表 having 筛选条件 order by 分组规则 limit 分页信息]
例:
#1. 查询出所有学生各科的成绩
mysql> select st.*,sc.score,su.name from students st left join score sc on st.id=sc.student_id left join subject su on sc.subject_id=su.id;
+----+-----------+--------+------+-------+--------+
| id | name | gender | age | score | name |
+----+-----------+--------+------+-------+--------+
| 1 | 张三 | 1 | 18 | 69 | NULL |
| 1 | 张三 | 1 | 18 | 90 | NULL |
| 1 | 张三 | 1 | 18 | NULL | 英语 |
| 1 | 张三 | 1 | 18 | 79 | 数学 |
| 1 | 张三 | 1 | 18 | 80 | 语文 |
| 2 | 李四 | 1 | 19 | 57 | NULL |
| 2 | 李四 | 1 | 19 | NULL | NULL |
| 2 | 李四 | 1 | 19 | 85 | 英语 |
| 2 | 李四 | 1 | 19 | 97 | 数学 |
| 2 | 李四 | 1 | 19 | 86 | 语文 |
| 3 | 周芷若 | 2 | 18 | 60 | NULL |
| 3 | 周芷若 | 2 | 18 | NULL | NULL |
| 3 | 周芷若 | 2 | 18 | NULL | 英语 |
| 3 | 周芷若 | 2 | 18 | NULL | 数学 |
| 3 | 周芷若 | 2 | 18 | 90 | 语文 |
| 4 | 赵敏 | 2 | 18 | 68 | NULL |
| 4 | 赵敏 | 2 | 18 | NULL | NULL |
| 4 | 赵敏 | 2 | 18 | 45 | 英语 |
| 4 | 赵敏 | 2 | 18 | 87 | 数学 |
| 4 | 赵敏 | 2 | 18 | 86 | 语文 |
| 5 | Lucy | 2 | 19 | NULL | NULL |
| 6 | Tony | 1 | 20 | NULL | NULL |
| 7 | Lucy | 2 | 20 | NULL | NULL |
+----+-----------+--------+------+-------+--------+
23 rows in set (0.00 sec)
#2. 查询出没有参加考试或总成绩为0的学生信息
mysql> select b.* from (select student_id from score group by student_id having sum(score)>0 and sum(score) is not null) a left join students b on a.student_id=b.id;
+----+------+--------+------+
| id | name | gender | age |
+----+------+--------+------+
| 5 | Lucy | 2 | 19 |
| 6 | Tony | 1 | 20 |
| 7 | Lucy | 2 | 20 |
+----+------+--------+------+
3 rows in set (0.02 sec)
#3. 查询出所有没有成绩的学生信息和没有成绩的科目
mysql> select st.*,sc.score,su.name from students st left join score sc on st.id=sc.student_id left join subject su on sc.subject_id=su.id where sc.score is null;
+----+-----------+--------+------+-------+--------+
| id | name | gender | age | score | name |
+----+-----------+--------+------+-------+--------+
| 1 | 张三 | 1 | 18 | NULL | 英语 |
| 2 | 李四 | 1 | 19 | NULL | NULL |
| 3 | 周芷若 | 2 | 18 | NULL | NULL |
| 3 | 周芷若 | 2 | 18 | NULL | 英语 |
| 3 | 周芷若 | 2 | 18 | NULL | 数学 |
| 4 | 赵敏 | 2 | 18 | NULL | NULL |
| 5 | Lucy | 2 | 19 | NULL | NULL |
| 6 | Tony | 1 | 20 | NULL | NULL |
| 7 | Lucy | 2 | 20 | NULL | NULL |
+----+-----------+--------+------+-------+--------+
#4. 查询出每个学生的平均成绩并按平均成绩倒序排列
mysql> select st.name 姓名, avg(sc.score) 平均成绩 from students st left join score sc on st.id=sc.student_id group by st.name order by 平均成绩 desc;
+-----------+--------------+
| 姓名 | 平均成绩 |
+-----------+--------------+
| 李四 | 81.2500 |
| 张三 | 79.5000 |
| 周芷若 | 75.0000 |
| 赵敏 | 71.5000 |
| Lucy | NULL |
| Tony | NULL |
+-----------+--------------+
6 rows in set (0.07 sec)
2)右外连接语法:
select 查询列表 from table_name1 right join table_name2 on 连接条件 [where 查询条件 group by 分组列表 having 筛选条件 order by 分组规则 limit 分页信息]
右外连接和左外连接基本一样,区别在于:
左外连接left左边的表为主表,右边的表为从表,右外连接与之相反
如:
select st.*,sc.score,su.name from score sc right join students st on st.id=sc.student_id left join subject su on sc.subject_id=su.id;
和左外连接的例1结果相同
3)全外连接:
MySQL中不支持full join语法,MySQL中可以使用union连接两个SQL语句,实现全外连接,如:
select st.*,sc.score from students st left join score sc on st.id=sc.student_id
union
select st.*,sc.score from students st right join score sc on st.id=sc.student_id;
+------+-----------+--------+------+-------+
| id | name | gender | age | score |
+------+-----------+--------+------+-------+
| 1 | 张三 | 1 | 18 | 69 |
| 1 | 张三 | 1 | 18 | 90 |
| 1 | 张三 | 1 | 18 | NULL |
| 1 | 张三 | 1 | 18 | 79 |
| 1 | 张三 | 1 | 18 | 80 |
| 2 | 李四 | 1 | 19 | 57 |
| 2 | 李四 | 1 | 19 | NULL |
| 2 | 李四 | 1 | 19 | 85 |
| 2 | 李四 | 1 | 19 | 97 |
| 2 | 李四 | 1 | 19 | 86 |
| 3 | 周芷若 | 2 | 18 | 60 |
| 3 | 周芷若 | 2 | 18 | NULL |
| 3 | 周芷若 | 2 | 18 | 90 |
| 4 | 赵敏 | 2 | 18 | 68 |
| 4 | 赵敏 | 2 | 18 | NULL |
| 4 | 赵敏 | 2 | 18 | 45 |
| 4 | 赵敏 | 2 | 18 | 87 |
| 4 | 赵敏 | 2 | 18 | 86 |
| 5 | Lucy | 2 | 19 | NULL |
| 6 | Tony | 1 | 20 | NULL |
| 7 | Lucy | 2 | 20 | NULL |
+------+-----------+--------+------+-------+
21 rows in set (0.00 sec)
union连接可以实现去重。
三、交叉连接:
语法:
select 查询列表 from table_name1 cross join table_name2;
交叉连接结果为笛卡尔乘积。
四、子查询
出现在其他语句中的select语句,称为子查询
#1. 查询出没有参加考试或总成绩为0的学生信息
select *
from students
where id in
(select student_id from score group by student_id having sum(score)>0 and sum(score) is not null); #子查询
+----+------+--------+------+
| id | name | gender | age |
+----+------+--------+------+
| 5 | Lucy | 2 | 19 |
| 6 | Tony | 1 | 20 |
| 7 | Lucy | 2 | 20 |
+----+------+--------+------+
3 rows in set (0.02 sec)