回顾:查询的语法
select 5
from 1
where 2
group by 3
having 4
order by 6
复杂查
- 分组查询
- 子查询
- 关联查询(表连接)
(1)分组查询
group by
having
使用group by子句的注意事项:
1)一旦按照某个字段分组,那么select子句后面只出现分组字段和聚合函数;
2)如果有where条件,那么一定在group by前面,它会过滤掉不满足条件的数据,
然后再在剩下的满足条件的数据中进行分组;
3)如果有order by排序,那么一定在group by的后面,是对分组后的结果集中的
数据进行排序;
4)如果分组后,想要筛选符合特定条件的组而不是所有组,那么需要在group by
后使用having,而不是where.
示例:
- 统计各个班级的人数
select * from xsb;
select bj,count(xh)
from xsb
group by bj;
- 统计班级人数多于2个人的班级及其数据
select bj,count(xh)
from xsb
group by bj
having count(xh)>2;
-
查询不止一门课程不及格的学员学号
select xh
from cjb
where cj<60
group by xh
having count(xh)>1;
(2)子查询
查询嵌套:查询语句里又嵌套了若干个小的查询
示例1:
查询选修了java语言的全部学员的平均成绩
思路:先通过“jva语言”这个课程名在kcb里找到它对应的kch,
然后再在cjb里根据kch查询到它所对应的cj,然后统计求平均值
select kch from kcb where kcm='java语言'; -- 02
select round(avg(cj)) from cjb where kch='02';
将上面两步进行合并:
select round(avg(cj))
from cjb
where kch=(
select kch
from kcb
where kcm='java语言'
);
示例2:
查询1班叫张三的学员的java语言的成绩
思路:
-
求出1班叫张三的学员的学号
select xh from xsb where bj='1班' and xm='张三'; -- 001
求出java语言对应的课程号
select kch from kcb where kcm='java语言'; -- 02
- 再在cjb里求出对应的成绩
select cj from cjb where xh='001' and kch='02';
将以上三步合并:
select cj from cjb
where xh=(
select xh from xsb
where bj='1班' and xm='张三'
)
and kch=(
select kch from kcb
where kcm='java语言'
);
示例3:
查询选修了java语言课程的所有学员的姓名和班级
思路:java语言——》在kcb里找到对应的kch——》cjb里找到对应的xh
——》在xsb里找到对应的xm和bj
select kch from kcb where kcm='java语言'; -- 02
select xh from cjb where kch='02'; -- 002 001
select xm,bj from xsb where xh in('001','002');
合并以上3步:
select xm,bj from xsb
where xh in(
select xh from cjb
where kch=(
select kch from kcb
where kcm='java语言'
)
);
使用子查询的注意事项:
1)子查询要用小括号括起来;
2)子查询经常用在where后做条件的一部分;
3)如果子查询返回多行,要用in连接,而不是=,不然会报错;
补充示例4:(不做要求)
查询java语言课程的分数高于这门课平均分的学员信息
思路:
求java语言的平均分,
然后查询java语言课程成绩项里高于java平均分的记录从而得到学号
最后再在xsb里找到对应的学员信息
select avg(cj) from cjb
where kch=(
select kch from kcb
where kcm='java语言'
);
select xh
from cjb
where kch=(
select kch from kcb
where kcm='java语言'
)
and cj>(
select avg(cj) from cjb
where kch=(
select kch from kcb
where kcm='java语言'
)
);
select * from xsb
where xh in(
select xh
from cjb
where kch=(
select kch from kcb
where kcm='java语言'
)
and cj>(
select avg(cj) from cjb
where kch=(
select kch from kcb
where kcm='java语言'
)
)
);
(3)关联查询(表连接)
引入:查询李四同学的各科成绩,要求查询结果要同时显示姓名、课程号、成绩
需要将原本不在同一张表的字段一起显示在同一个查询结果集里面,通过表连接来实现。
表怎么连接?通过笛卡儿积运算连接(将第一张表的每一行跟第二张表里的每一行一一组合,
从而生成一张大的表的过程)——》在SQL里,from后面跟了多张表的时候就是在做笛卡儿积运算,生成一张大的表:select * from xsb,cjb;——》这张大的表里存在许多没有意义的垃圾数据(例如002号学员连接了001号学员的成绩)——》如何过滤这些垃圾数据?——》通过添加关联条件的方式进行垃圾数据的消除——》我们希望002号学员连接002号学员的各科成绩,那么可以通过让xsb的xh跟cjb的xh相等进行垃圾数据的消除。
select * from xsb,cjb; -- 132行(11*12)11列(8+3)
select * from xsb; -- 11行8列
select * from cjb; -- 12行3列
有两种添加关联条件的写法
简写方式:
select * from xsb,cjb
where xsb.xh=cjb.xh;
标准写法:
select * from xsb inner join cjb
on xsb.xh=cjb.xh;
回到一开始的查询需求:
查询李四同学的各科成绩,要求查询结果要同时显示姓名、课程号、成绩
简写方式:
select xsb.xm,cjb.kch,cjb.cj
from xsb,cjb
where xsb.xh=cjb.xh
and xsb.xm='李四';
标准写法:
select xsb.xm,cjb.kch,cjb.cj
from xsb inner join cjb
on xsb.xh=cjb.xh
where xsb.xm='李四';
表连接的注意事项:
1)对于同名字段,我们需要在字段名前加上表名做前缀以示区分;
对于非同名字段,可以不加表名做前缀,但是,建议初学者加上以避免错误。
2)只要看到了from后面跟两张及其以上的表,就要反应出这是在做笛卡儿积运算,
会生成一张大的表,笛卡儿积运算会产生垃圾数据,需要添加关联条件来消除垃圾数据;
3)如果题目中的查询需求除了表的关联以外还有其他的限定条件,我们可以通过and连接
其他的条件(表的连接的简写方式),对于inner join这种标准写法,我们把条件放在where
子句里
4)除了可以给字段取别名以外,还可以给表取别名
select x.xm,c.kch,c.cj
from xsb x,cjb c
where x.xh=c.xh
and x.xm='李四';
select x.xm,c.kch,c.cj
from xsb x inner join cjb c
on x.xh=c.xh
where x.xm='李四';
表的连接方式
- 内连接:inner join,只返回满足关联条件的结果集
外连接:
- 左外连接:left join/left outer join,指的是除了返回满足关联条件的结果集以外,还会把
left join左边的那张表完整的展示出来,右边的那张表里不满足关联条件的字段位置补空值(null)
- 右外连接:right join/right outer join,指的是除了返回满足关联条件的结果集以外,还会把right join右边的那张表完整的展示出来,左边的那张表里不满足关联条件的字段位置补空值(null)
- 全外连接:full join/full outer join,指的是除了返回满足关联条件的结果集以外,还会把
full join两边的表完整的展示出来,两边不满足关联条件的字段位置补空值(null)
注意:MySQL不支持full join,可以通过union集合操作来实现全外连接
外连接示例:
1、查询所有学员的学号、姓名、课程号和成绩(要求没有选课的学员信息也要展示出来)
以下这种写法不能将没有选课的学员信息展示出来(例如008/009等学员)
select x.xh,x.xm,c.kch,c.cj
from xsb x,cjb c
where x.xh=c.xh;
select * from xsb;
2、可以通过左外连接或者右外连接来实现这个需求(需要将xsb信息完整展示)
- 左外连接来实现:
- 例子: 查询公司员工的姓名、年龄及工资,没有工资的员工信息也要展示;
- select gyb.gh,gyb.xm,gyb.nl,gzb.gz from gyb left join gzb on gyb.gh=gzb.gh;
- gyb展示出来,gzb没有的补空值。
select x.xh,x.xm,c.kch,c.cjfrom xsb x left join cjb c on x.xh=c.xh; 展示left join左边的表xsb x
select x.xh,x.xm,c.kch,c.cj
from xsb x left join cjb c
on x.xh=c.xh; 展示left join左边的表xsb x
- 右外连接来实现:
select x.xh,x.xm,c.kch,c.cj
from cjb c right join xsb x
on x.xh=c.xh;
全外连接:
select x.xh,x.xm,c.kch,c.cj
from cjb c right join xsb x
on x.xh=c.xh
union
select x.xh,x.xm,c.kch,c.cj
from cjb c left join xsb x
on x.xh=c.xh;
三表关联的示例:
查询学员的姓名、选修的课程名和成绩
select x.xm,k.kcm,c.cj
from xsb x,kcb k,cjb c
where x.xh=c.xh
and k.kch=c.kchselect x.xm,k.kcm,c.cj
from xsb x inner join cjb c
on x.xh=c.xh
inner join kcb k
on k.kch=c.kch;
补充:限制结果输出 可以输出员工数量
如果你只想返回一定数量的行,可以通过limit子句来实现。
limit子句常见的用法:
limit n 等同于limit 0,n
limit m,n 指的是从记录编号m开始的n条记录/数据(记录的编号从0开始)
select * from xsb;
-
查询前三条记录:
select * from xsb limit 0,3;
select * from xsb limit 3;
-
查询年龄最大的三个学员
select * from xsb
order by nl desc
limit 3;
SQL查询语句的执行顺序小结:
1 from 如果是一张表,就将这张表的数据查询出来,如果是多张表,则执行笛卡儿积运算
2 on 进行关联条件的筛选
3 join 如果指定了外连接,会关联那些没有匹配关联条件的记录进行
4 where 筛选满足where条件的记录
5 group by 分组
6 having 筛选满足having条件的组
7 select 筛选出特定的字段
8 distinct 去重
9 order by 排序
10 limit 取出指定数量的记录