一、表关系

先参照如下表结构创建7张表格,并创建相关约束

               
班级表:class       学生表:student      
cid caption grade_id   sid sname gender class_id
1 一年一班 1   1 乔丹 1
2 二年一班 2   2 艾弗森 1
3 三年二班 3   3 科比 2
               
老师表:teacher       课程表:course      
tid tname     cid cname teacher_id  
1 张三     1 生物 1  
2 李四     2 体育 1  
3 王五     3 物理 2  
               
成绩表:score        

年级表:

class_grade

   
sid student_id course_id score   gid gname  
1 1 1 60   1 一年级  
2 1 2 59   2 二年级  
3 2 2 99   3 三年级  
               
班级任职表:teach2cls              
tcid tid cid          
1 1 1          
2 1 2          
3 2 1          
4 3 2        

1. 班级表class

create table class
(
cid int primary key auto_increment,
caption char(10),
grade_id int
);

【创建表语句】

insert into class values
(1,'少一一班',1),
(2,'少二一班',2),
(3,'少三二班',3),
(4,'少四一班',4),
(5,'少五三班',5);

【插入记录语句】

2. 学生表student

create table student
(
sid int primary key auto_increment,
sname char(10),
gender enum('男','女') not null,
class_id int
);

【创建表语句】

insert into student values
(1,'乔丹','女',1),
(2,'艾弗森','女',1),
(3,'科比','男',2),
(4,'葫芦娃','男',3),
(5,'张三丰','男',5),
(6,'洞房不败','男',4),
(7,'樱木花道','男',2),
(8,'松岛菜菜子','女',3),
(9,'洞房不败','女',5);

【插入记录语句】

3. 老师表teacher

create table teacher
(
tid int primary key auto_increment,
tname char(10)
);

【创建表语句】

insert into teacher values
(1,'张三'),
(2,'李四'),
(3,'王五'),
(4,'萧峰'),
(5,'一休哥'),
(6,'诸葛'),
(7,'李四');

【插入记录语句】

4. 课程表course

create table course
(
cid int primary key auto_increment,
cname char(10),
teacher_id int
);

【创建表语句】

insert into course values
(1,'生物',1),
(2,'体育',1),
(3,'物理',2),
(4,'数学',3),
(5,'语文',4),
(6,'英语',2),
(7,'土遁?沙地送葬',5),
(8,'夏日喂蚊子大法',3),
(9,'麻将牌九扑克千术',6);

【插入记录语句】

5. 成绩表score

create table score
(
sid int primary key auto_increment,
student_id int,
course_id int,
score int
);

【创建表语句】

insert score values
(1,1,1,60),
(2,1,2,21),
(3,2,2,99),
(4,3,3,56),
(5,4,1,56),
(6,5,3,94),
(7,5,4,40),
(8,6,4,80),
(9,7,3,37),
(10,8,5,100),
(11,8,6,89),
(12,8,7,0),
(13,3,8,45),
(14,7,1,89),
(15,2,7,89),
(16,2,1,61);

【插入记录语句】

6. 年级表class_grade

create table class_grade
(
gid int primary key auto_increment,
gname char(10)
);

【创建表语句】

insert class_grade values
(1,'少一年级'),
(2,'少二年级'),
(3,'少三年级'),
(4,'少四年级'),
(5,'少五年级');

【插入记录语句】

7. 班级任职表teach2cls

create table teach2cls
(
tcid int primary key auto_increment,
tid int,
cid int
);

【创建表语句】

insert into teach2cls values
(1,1,1),
(2,1,2),
(3,2,1),
(4,3,2),
(5,4,5),
(6,5,3),
(7,5,5),
(8,6,2),
(9,6,4),
(10,6,3),
(11,4,1),
(12,1,4);

【插入记录语句】

二、操作表

★注:由于样本数量有限,为了能够得到足够的查询结果,所有题目中涉及到“超过”或“以上”字样的,均默认为包含该值

(例如:查询教授课程超过2门的老师的id和姓名,视作教授课程数>=2)

1、自行创建测试数据;

(创建语句见"一、表关系")

2、查询学生总人数

select
count(*) as 学生总人数
from
student;

3、查询“生物”课程和“物理”课程成绩都及格的学生id和姓名

【查法1——子查询】

select
sid, sname
from
student
where
sid in
(
select
student_id
from
score
where
student_id in
(select
student_id
from
score
where
course_id = (select cid from course where cname = '生物')
and
score >= 60)
and
course_id =
(select cid from course where cname = '物理')
and
score >= 60
);

【查法2——联表】

select
sid,
sname
from
student
where
sid in
(
select
t1.student_id
from
(
select
student_id
from
score
where
course_id = (select cid from course where cname = '生物')
and
score >= 60
) as t1
inner join
(
select
student_id
from
score
where
course_id = (select cid from course where cname = '物理')
and
score >= 60
) as t2
on
t1.student_id=t2.student_id
);

4、查询每个年级的班级数,取出班级数最多的前三个年级

select
class.grade_id,
class_grade.gname,
count(class.cid) as 班级数
from
class inner join class_grade
on class.grade_id=class_grade.gid
group by
class.grade_id
order by
count(class.cid) desc
limit 3;

5、查询平均成绩最高和最低的学生的id和姓名以及平均成绩

select
stu.sid,
stu.sname,
avg(score) as 平均成绩
from
student as stu inner join score as sco
on stu.sid = sco.student_id
group by
stu.sid
having
avg(score) =
(
select
avg(score)
from
score
group by
student_id
order by
avg(score) desc
limit 1
)
or
avg(score) =
(
select
avg(score)
from
score
group by
student_id
order by
avg(score) asc
limit 1
);

6、查询每个年级的学生人数

select
t1.gname,
count(s.sid) as 学生人数
from
(
select
*
from
class as c inner join class_grade as g
on c.grade_id = g.gid
) as t1
inner join
student as s
on
t1.cid = s.class_id
group by
t1.gid;

7、查询每位学生的学号,姓名,选课数,平均成绩

select
stu.sid as 学号,
stu.sname as 姓名,
count(sco.course_id) as 选课数,
avg(sco.score) as 平均成绩
from
student as stu left join score as sco
on stu.sid = sco.student_id
group by
sco.student_id;

8、查询学生编号为“2”的学生的姓名、该学生成绩最高的课程名、成绩最低的课程名及分数

select
t1.sname as 姓名,
t2.cname as 课程名,
t1.score as 分数
from
(select
stu.sid, stu.sname, sco.course_id, sco.score
from
student as stu inner join score as sco
on stu.sid = sco.student_id
where stu.sid=2) as t1
inner join
course as t2
on
t1.course_id = t2.cid
group by
t2.cid
having
score in (max(score),min(score));

9、查询姓“李”的老师的个数和所带班级数;

select
count(te.tid) as 姓李老师个数,
count(tc.cid) as 所带班级数
from
teacher as te inner join teach2cls as tc
on te.tid = tc.tid
where
te.tname regexp "^李.*"
group by
te.tid;

10、查询班级数小于5的年级id和年级名;

select
c.grade_id as 年级id,
g.gname as 年级名
from
class as c inner join class_grade as g
on c.grade_id = g.gid
group by
c.grade_id
having
count(c.cid)<5;

11、查询班级信息,包括班级id、班级名称、年级、年级级别(12为低年级,34为中年级,56为高年级),示例结果如下;

select
cid as 班级id,
caption as 班级名称,
gname as 年级,
case
when g.gid in (1,2) then '低年级'
when g.gid in (3,4) then '中年级'
when g.gid in (5,6) then '高年级'
else '其他'
end as 年级级别
from
class as c inner join class_grade as g
on c.grade_id = g.gid;

12、查询学过“张三”老师2门课以上的同学的学号、姓名;

select
stu.sid as 学号,
stu.sname as 姓名
from
student as stu inner join score as sco
on stu.sid = sco.student_id
where
sco.course_id in
(
select
c.cid
from
teacher as t inner join course as c
on t.tid = c.teacher_id
where
t.tname = '张三'
)
group by
stu.sid
having
count(sco.course_id) >= 2;

13、查询教授课程超过2门的老师的id和姓名;

select
tid as id,
tname as 姓名
from
teacher as t inner join course as c
on t.tid = c.teacher_id
group by
c.teacher_id
having
count(c.cid) >= 2;

14、查询学过编号“1”课程和编号“2”课程的同学的学号、姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
student_id in
(
select
student_id
from
score
where
course_id = 1
)
and
course_id = 2
);

15、查询没有带过高年级的老师id和姓名;

select
tid as 老师id,
tname as 姓名
from
teacher
where
tid not in
(
select
tc.tid
from
class as c inner join teach2cls as tc
on c.cid = tc.cid
where
c.grade_id in (5,6)
);

16、查询学过“张三”老师所教的所有课的同学的学号、姓名;

select
distinct
stu.sid as 学号,
stu.sname as 姓名
from
student as stu inner join score as sco
on stu.sid = sco.student_id
where
sco.course_id in
(
select
c.cid
from
teacher as t inner join course as c
on t.tid = c.teacher_id
where
t.tname = "张三"
);

17、查询带过超过2个班级的老师的id和姓名;

select
tid as id,
tname as 姓名
from
teacher
where
tid in
(
select
tid
from
teach2cls
group by
tid
having
count(cid) >= 2
);

18、查询课程编号“2”的成绩比课程编号“1”课程低的所有同学的学号、姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
t1.student_id
from
(
select
*
from
score
where
course_id = 1
) as t1
inner join
(
select
*
from
score
where
course_id = 2
) as t2
on
t1.student_id = t2.student_id
where
t1.score > t2.score
);

19、查询所带班级数最多的老师id和姓名;

select
tid as id,
tname as 姓名
from
teacher
where
tid in
(
select
tid
from
teach2cls
group by
tid
having
count(cid) =
(
select
count(cid)
from
teach2cls
group by
tid
order by
count(cid) desc
limit 1
)
);

20、查询有课程成绩小于60分的同学的学号、姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
score < 60
);

21、查询没有学全所有课的同学的学号、姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
group by
student_id
having
count(course_id) != (select count(cid) from course)
);

22、查询至少有一门课与学号为“1”的同学所学相同的同学的学号和姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
course_id in
(
select
course_id
from
score
where
student_id = 1
)
);

23、查询至少学过学号为“1”同学所选课程中任意一门课的其他同学学号和姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
course_id in
(
select
course_id
from
score
where
student_id = 1
)
and
student_id != 1
);

24、查询和“2”号同学学习的课程完全相同的其他同学的学号和姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(select
student_id
from
score
where
student_id != 2
group by
student_id
having
group_concat(course_id order by course_id asc) =
(select
group_concat(course_id order by course_id asc)
from
score
where
student_id = 2
group by
student_id)
);

25、删除学习“张三”老师课的score表记录;

delete
from
score
where
course_id in
(
select
c.cid
from
teacher as t inner join course as c
on t.tid = c.teacher_id
where
t.tname = '张三'
);

26、向score表中插入一些记录,这些记录要求符合以下条件:

①没有上过编号“2”课程的同学学号;②插入“2”号课程的平均成绩;

【插入第一条】

insert into
score(student_id, course_id, score)
values
(
(
select
sid
from
student
where
sid not in
(
select
s.student_id
from
score as s
where
s.course_id = 2
)
order by
sid desc
limit 0,1
),
2,
(
select
avg(s.score)
from
score as s
where
s.course_id = 2
)
);

【插入第二条】

insert into
score(student_id, course_id, score)
values
(
(
select
sid
from
student
where
sid not in
(
select
s.student_id
from
score as s
where
s.course_id = 2
)
order by
sid desc
limit 1,1
),
2,
(
select
avg(s.score)
from
score as s
where
s.course_id = 2
)
);

【改limit后的第一个参数值,可继续插入第三、四、...条】

27、按平均成绩从低到高显示所有学生的“语文”、“数学”、“英语”三门的课程成绩,按如下形式显示: 学生ID,语文,数学,英语,有效课程数,有效平均分;

【这里题目有歧义:有效课程数和有效平均分是仅以这3门课来统计,还是以学生所有科目来统计】

【解一:仅以这3门课来统计】

select
t2.sid as 学生ID,
sum(case when t1.cname = '语文' then t1.score else null end) as 语文,
sum(case when t1.cname = '数学' then t1.score else null end) as 数学,
sum(case when t1.cname = '英语' then t1.score else null end) as 英语,
count(case when t1.cname in ('语文','数学','英语') then 1 else null end) as 有效课程数,
avg(case when t1.cname in ('语文','数学','英语') then t1.score else null end) as 有效平均分
from
(select * from score as s inner join course as c
on s.course_id = c.cid) as t1
right join
student as t2
on
t1.student_id = t2.sid
group by
t2.sid
order by
avg(case when t1.cname in ('语文','数学','英语') then t1.score else null end) asc;

【解二:以该学生所有科目来统计】

select
t2.sid as 学生ID,
sum(case when t1.cname = '语文' then t1.score else null end) as 语文,
sum(case when t1.cname = '数学' then t1.score else null end) as 数学,
sum(case when t1.cname = '英语' then t1.score else null end) as 英语,
count(t1.score) as 有效课程数,
avg(t1.score) as 有效平均分
from
(select * from score as s inner join course as c
on s.course_id = c.cid) as t1
right join
student as t2
on
t1.student_id = t2.sid
group by
t2.sid
order by
avg(t1.score) asc;

28、查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;

select
course_id as 课程ID,
max(score) as 最高分,
min(score) as 最低分
from
score
group by
course_id;

29、按各科平均成绩从低到高和及格率的百分数从高到低顺序;

【这里优先按平均成绩从低到高排序,若遇到平均成绩相同的则按及格率百分数从高到低排序】

select
course_id as 课程ID,
avg(score) as 平均成绩,
concat(100*count(case when score>=60 then 1 else null end)/count(score),"%") as 及格率
from
score
group by
course_id
order by
avg(score) asc,
count(case when score>=60 then 1 else null end)/count(score) desc;

30、课程平均分从高到低显示(显示任课老师);

select
t1.cname as 课程名称,
avg(t2.score) as 平均分,
t1.tname as 任课老师
from
(select * from teacher as t inner join course as c
on t.tid = c.teacher_id) as t1
inner join
score as t2
on
t1.cid = t2.course_id
group by
t2.course_id
order by
avg(t2.score) desc;

31、查询各科成绩前三名的记录(不考虑成绩并列情况)

【本题与44题类似,不会做,于是百度了下"如何在mysql中查询每个分组的前几名",参照其中的一个比较优雅的方法,写出了答案】

【注:这里仍然是按照score表默认的排序,即sid的排序】

select
*
from
score
where
(
select
count(*)
from
score as s
where
s.course_id = score.course_id
and
s.score <= score.score
)
<= 3;

32、查询每门课程被选修的学生数;

select
cname as 课程名,
count(s.student_id) as 选修学生数
from
course as c left join score as s
on c.cid = s.course_id
group by
c.cid;

33、查询选修了2门以上课程的全部学生的学号和姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
group by
student_id
having
count(course_id) >= 2
);

34、查询男生、女生的人数,按倒序排列;

select
gender, count(sid)
from
student
group by
gender
order by
count(sid) desc;

35、查询姓“张”的学生名单;

【查法1——正则】

select
sname
from
student
where
sname regexp "^张.*";

【查法2——like】

select
sname
from
student
where
sname like "张%";

36、查询同名同姓学生名单,并统计同名人数;

select
sname as 姓名,
count(sid) as 同名人数
from
student
group by
sname
having
count(sid) > 1;

37、查询每门课程的平均成绩,结果按平均成绩升序排列,平均成绩相同时,按课程号降序排列;

select
avg(score),course_id
from
score
group by
course_id
order by
avg(score) asc, course_id desc;

38、查询课程名称为“数学”,且分数低于60的学生姓名和分数;

select
stu.sname as 学生姓名,
sco.score as 分数
from
student as stu inner join score as sco
on stu.sid = sco.student_id
where
sco.course_id =
(
select
cid
from
course
where
cname = '数学'
)
and
sco.score < 60;

39、查询课程编号为“3”且课程成绩在80分以上的学生的学号和姓名;

select
sid as 学号,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
course_id = 3
and
score >= 80
);

40、求选修了课程的学生人数

select
count(1) as 学生人数
from
(
select
distinct student_id
from
score
) as t1;

41、查询选修“王五”老师所授课程的学生中,成绩最高和最低的学生姓名及其成绩;

select
stu.sname as 学生姓名,
sco.score as 成绩
from
student as stu inner join score as sco
on stu.sid = sco.student_id
where
score in
(
(
select
max(score)
from
score
where
course_id in
(
select
c.cid
from
teacher as t inner join course as c
on t.tid = c.teacher_id
where
t.tname = '王五'
)
),
(
select
min(score)
from
score
where
course_id in
(
select
c.cid
from
teacher as t inner join course as c
on t.tid = c.teacher_id
where
t.tname = '王五'
)
)
);

42、查询各个课程及相应的选修人数;

select
cname as 课程名,
count(s.student_id) as 选修学生数
from
course as c left join score as s
on c.cid = s.course_id
group by
c.cid;

43、查询不同课程但成绩相同的学生的学号、课程号、学生成绩;

select
student_id as 学号,
course_id as 课程号,
score as 学生成绩
from
score
group by
score
having
count(student_id) > 1;

44、查询每门课程成绩最好的前两名学生id和姓名;

【注:这里指定了前两名,所以若出现多名同分的学生也只取倒序排的默认前2名】

【与31题类似…不会写,于是百度了下"如何在mysql中查询每个分组的前几名",参照其中一种比较高端且高效的自定义变量的方法,写出了答案】

(但下面这个答案因为是一个从分组中查询非分组字段的查询,它只能在 ONLY_FULL_GROUP_BY 模式关闭(链接)的情况下才能起作用。)

set @num := 0, @cname := '';
select
t.cid as 课程ID,
t.cname as 课程名,
t.sid as 学生ID,
t.sname as 学生名,
t.score as 成绩,
@num := if(@cname = t.cname, @num + 1, 1) as 排名,
@cname := t.cname as 课程名确认
from
(
SELECT
*
FROM
(
select
stu.sid, stu.sname, sco.course_id, sco.score
from
student as stu inner join score as sco
on stu.sid = sco.student_id
ORDER BY
sco.score
) as t1
right join
course as t2
on
t1.course_id = t2.cid
) as t
group by
t.cid, t.score, t.sname
having
排名 <= 2
;

但是导师不推荐这种写法,于是附上导师的参考答案吧

SELECT
c.sid,
a.course_id,
c.sname,
d.cname,
a.score
FROM
score a
INNER JOIN (
SELECT
course_id,
score,
rank
FROM
(
SELECT
a.course_id,
a.score,
count(*) AS rank
FROM
(
SELECT
course_id,
score
FROM
score
GROUP BY
course_id,
score
ORDER BY
course_id,
score DESC
) a
INNER JOIN (
SELECT
course_id,
score
FROM
score
GROUP BY
course_id,
score
ORDER BY
course_id,
score DESC
) b ON a.course_id = b.course_id
AND a.score <= b.score
GROUP BY
course_id,
score
) t1
WHERE
rank IN (1, 2)
ORDER BY
course_id,
rank
) b ON a.course_id = b.course_id
AND a.score = b.score
INNER JOIN student c ON a.student_id = c.sid
INNER JOIN course d ON a.course_id = d.cid
ORDER BY
course_id,
score DESC,
sid ASC;

45、检索至少选修两门课程的学生学号;

select
sid as 学号
from
student
where
sid in
(
select
student_id
from
score
group by
student_id
having
count(course_id) >= 2
);

46、查询没有学生选修的课程的课程号和课程名;

select
cid as 课程号,
cname as 课程名
from
course
where
cid not in
(
select
distinct course_id
from
score
);

47、查询没带过任何班级的老师id和姓名;

select
tid as 老师id,
tname as 姓名
from
teacher
where
tid not in
(
select
distinct tid
from
teach2cls);

48、查询有两门以上课程超过80分的学生id及其平均成绩;

select
student_id as 学生id,
avg(score) as 平均成绩
from
score
where
student_id in
(
select
student_id
from
score
where
score >= 80
group by
student_id
having
count(course_id) >= 2
)
group by
student_id;

49、检索“3”课程分数小于60,按分数降序排列的同学学号;

select distinct
student_id as 学号
from
score
where
course_id = 3 and score < 60
order by
score desc;

50、删除编号为“2”的同学的“1”课程的成绩;

delete
from
score
where
student_id = 2 and course_id = 1;

51、查询同时选修了物理课和生物课的学生id和姓名;

select
sid as 学生id,
sname as 姓名
from
student
where
sid in
(
select
student_id
from
score
where
course_id = (select cid from course where cname = '生物')
)
and
sid in
(
select
student_id
from
score
where
course_id =
(
select
cid
from
course
where
cname = '物理'
)
);

三、作业引申

★count(*)、count(1)与count(COL)的抉择?

【参考文章】Select count(*)和Count(1)的区别和执行方式

  往常我经常会看到一些所谓的优化建议不使用Count(* )而是使用Count(1),从而可以提升性能,给出的理由是Count( *)会带来全表扫描。而实际上如何写Count并没有区别。

  实际上 count 的意思是,评估count()中的表达式是否为NULL,如果括号中表达式为NULL则结果不计数(计为0),而括号中表达式非NULL则会计数。

  1、比如我们看下面的所示,在 count 中指定NULL(优化器不允许显式指定NULL,因此需要赋值给自定义变量才能指定)。

SET @xx=NULL

SELECT COUNT(@xx) FROM class;

  由于这里相当于对所有行都计为NULL,所以结果全部计数为0,结果如下图所示。

+------------+
| count(@xx) |
+------------+
| 0 |
+------------+
1 row in set (0.00 sec)

  2、因此当你指定Count(*)或者Count(1)或者无论Count(‘anything’)时结果都会一样,因为括号里这些值都不为NULL,语句如下图所示。

select count(*) from class;

select count(1) from class;

select count('anything') from class;

  运行结果都一样:

+----------+
| count(*) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec) +----------+
| count(1) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec) +-------------------+
| count('anything') |
+-------------------+
| 5 |
+-------------------+
1 row in set (0.00 sec)

  3、那么count(COL)对某一列(字段)进行计数呢?

  对于Count(列)来说,同样适用于上面规则,评估括号内的列中每一行的值是否为NULL,如果某行为NULL则该行不计数,某行不为NULL则该行计数。因此Count(列)会计算列或这列的组合不为空的计数。

  例如下面这张test表:

+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| NULL | NULL | NULL |
+------+------+------+
3 rows in set (0.00 sec)

  我们使用count(a) 对a列进行计数,语句和结果如下:

select count(a) from test;

+----------+
| count(a) |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec)

  而我们用count(*)(或count(1)、count('anything')等)则计数为3:

select count(*) from test;

+----------+
| count(*) |
+----------+
| 3 |
+----------+
1 row in set (0.00 sec)

【总结】

  如果我们要统计有多少条记录(连全为null的记录也算),则直接用count(*)或count(1)都可以;

  如果我们要统计某一列有多少条有效记录(为null的记录/行不算),则用count(列)的方式。

★group_concat的具体玩法?

【参考文章】mysql之group_concat函数详解

  group_concat函数的具体语法如下:

group_concat( [DISTINCT]  要连接的字段   [Order BY 排序字段 ASC/DESC]   [Separator '分隔符'] )

  下面举例说明

select * from goods;

+------+-------+
| id | price |
+------+-------+
| 1 | 10 |
| 1 | 20 |
| 1 | 20 |
| 2 | 20 |
| 3 | 200 |
| 3 | 500 |
+------+-------+
6 rows in set (0.00 sec)

  group_concat的基本功能是:以id分组,把price字段的值在同一行打印出来,逗号分隔(默认)

select id, group_concat(price) from goods group by id;

+------+---------------------+
| id | group_concat(price) |
+------+---------------------+
| 1 | 10,20,20 |
| 2 | 20 |
| 3 | 200,500 |
+------+---------------------+
3 rows in set (0.00 sec)

  对于这样的查询结果,我们可以使用group_concat的参数对结果进行一些处理:

  1、以id分组,把price字段的值在一行打印出来,改为"/"号分隔

select id, group_concat(price separator "/") from goods group by id;

+------+-----------------------------------+
| id | group_concat(price separator "/") |
+------+-----------------------------------+
| 1 | 10/20/20 |
| 2 | 20 |
| 3 | 200/500 |
+------+-----------------------------------+
3 rows in set (0.00 sec)

  2、以id分组,把去除重复冗余的price字段的值打印在一行,逗号分隔

select id,group_concat(distinct price) from goods group by id; 

+------+------------------------------+
| id | group_concat(distinct price) |
+------+------------------------------+
| 1 | 10,20 |
| 2 | 20 |
| 3 | 200,500 |
+------+------------------------------+
3 rows in set (0.00 sec)

  3、以id分组,把price字段的值打印在一行,逗号分隔,按照price倒序排列

select id,group_concat(price order by price desc) from goods group by id;

+------+-----------------------------------------+
| id | group_concat(price order by price desc) |
+------+-----------------------------------------+
| 1 | 20,20,10 |
| 2 | 20 |
| 3 | 500,200 |
+------+-----------------------------------------+
3 rows in set (0.00 sec)

★SELECT CASE WHEN的具体玩法?

【参考文章】CASE WHEN 及 SELECT CASE WHEN的用法

  case when能为我们提供什么样的玩法呢?

  1、已知数据按照另外一种方式进行分组,分析。

  例如:根据如下的国家人口数据,统计亚洲和北美洲的人口数量。

  先创建表格,并插入数据

create table population(
country char(20) primary key,
population int);

创建表格

insert into population values
('中国', 600),
('美国', 100),
('加拿大',100),
('英国', 200),
('法国', 300),
('日本', 250),
('德国', 200),
('墨西哥', 50),
('印度', 250);

插入数据

  得到表格:

+-----------+------------+
| country | population |
+-----------+------------+
| 中国 | 600 |
| 加拿大 | 100 |
| 印度 | 250 |
| 墨西哥 | 50 |
| 德国 | 200 |
| 日本 | 250 |
| 法国 | 300 |
| 美国 | 100 |
| 英国 | 200 |
+-----------+------------+
9 rows in set (0.00 sec)

  用CASE WHEN ELSE END对字段进行分类处理:

SELECT
CASE country
WHEN '中国' THEN '亚洲'
WHEN '印度' THEN '亚洲'
WHEN '日本' THEN '亚洲'
WHEN '美国' THEN '北美洲'
WHEN '加拿大' THEN '北美洲'
WHEN '墨西哥' THEN '北美洲'
ELSE '其他'
END as '洲',
SUM(population) as '人口'
FROM
population
GROUP BY
CASE country
WHEN '中国' THEN '亚洲'
WHEN '印度' THEN '亚洲'
WHEN '日本' THEN '亚洲'
WHEN '美国' THEN '北美洲'
WHEN '加拿大' THEN '北美洲'
WHEN '墨西哥' THEN '北美洲'
ELSE '其他'
END;

  结果如下:

+-----------+--------+
| 洲 | 人口 |
+-----------+--------+
| 亚洲 | 1100 |
| 其他 | 700 |
| 北美洲 | 250 |
+-----------+--------+
3 rows in set (0.00 sec)

【注】题目11——查询班级信息,包括班级id、班级名称、年级、年级级别(12为低年级,34为中年级,56为高年级)

    解法就是用到这种方法

select
cid as 班级id,
caption as 班级名称,
gname as 年级,
case
when g.gid in (1,2) then '低年级'
when g.gid in (3,4) then '中年级'
when g.gid in (5,6) then '高年级'
else '其他'
end as 年级级别
from
class as c inner join class_grade as g
on c.grade_id = g.gid;
+----------+--------------+-----------+--------------+
| 班级id | 班级名称 | 年级 | 年级级别 |
+----------+--------------+-----------+--------------+
| 1 | 一年一班 | 一年级 | 低年级 |
| 2 | 二年一班 | 二年级 | 低年级 |
| 3 | 三年二班 | 三年级 | 中年级 |
| 5 | 少四一班 | 少五 | 中年级 |
| 4 | 少五三班 | 少四 | 高年级 |
+----------+--------------+-----------+--------------+
5 rows in set (0.00 sec)

  2、用一个SQL语句完成不同条件的分组计数

  例如:对下述数据,按照国家和性别进行分组统计。

  先创建表格,并插入数据

create table population2(
country char(20),
sex int,
population int);

创建表格

insert into population2 values
('中国', 1, 340),
('中国', 2, 260),
('美国', 1, 45),
('美国', 2, 55),
('加拿大',1, 51),
('加拿大',2, 49),
('英国', 1, 40),
('英国', 2, 60);

插入数据

  用CASE WHEN ELSE END对数据进行国家和性别的分组计数:

SELECT
country as '国家',
SUM( CASE WHEN sex = '' THEN population ELSE 0 END) as '男',
SUM( CASE WHEN sex = '' THEN population ELSE 0 END) as '女'
FROM
population2
GROUP BY
country;

  得到分组计数结果:

+-----------+------+------+
| 国家 | 男 | 女 |
+-----------+------+------+
| 中国 | 340 | 260 |
| 加拿大 | 51 | 49 |
| 美国 | 45 | 55 |
| 英国 | 40 | 60 |
+-----------+------+------+
4 rows in set (0.00 sec)

【注】题目27——按平均成绩从低到高显示所有学生的“语文”、“数学”、“英语”三门的课程成绩,按如下形式显示:学生ID,语文,数学,英语,有效课程数,有效平均分;

    解法就是用到这种方法

【这里题目有歧义:有效课程数和有效平均分是仅以这3门课来统计,还是以学生所有科目来统计】
【解一:仅以这3门课来统计】
select
t2.sid as 学生ID,
sum(case when t1.cname = '语文' then t1.score else null end) as 语文,
sum(case when t1.cname = '数学' then t1.score else null end) as 数学,
sum(case when t1.cname = '英语' then t1.score else null end) as 英语,
count(case when t1.cname in ('语文','数学','英语') then 1 else null end) as 有效课程数,
avg(case when t1.cname in ('语文','数学','英语') then t1.score else null end) as 有效平均分
from
(select * from score as s inner join course as c
on s.course_id = c.cid) as t1
right join
student as t2
on
t1.student_id = t2.sid
group by
t2.sid
order by
avg(case when t1.cname in ('语文','数学','英语') then t1.score else null end) asc; 【解二:以该学生所有科目来统计】
select
t2.sid as 学生ID,
sum(case when t1.cname = '语文' then t1.score else null end) as 语文,
sum(case when t1.cname = '数学' then t1.score else null end) as 数学,
sum(case when t1.cname = '英语' then t1.score else null end) as 英语,
count(t1.score) as 有效课程数,
avg(t1.score) as 有效平均分
from
(select * from score as s inner join course as c
on s.course_id = c.cid) as t1
right join
student as t2
on
t1.student_id = t2.sid
group by
t2.sid
order by
avg(t1.score) asc;
【解一结果】
+----------+--------+--------+--------+-----------------+-----------------+
| 学生ID | 语文 | 数学 | 英语 | 有效课程数 | 有效平均分 |
+----------+--------+--------+--------+-----------------+-----------------+
| 2 | 0 | 0 | 0 | 0 | NULL |
| 9 | 0 | 0 | 0 | 0 | NULL |
| 3 | 0 | 0 | 0 | 0 | NULL |
| 7 | 0 | 0 | 0 | 0 | NULL |
| 4 | 0 | 0 | 0 | 0 | NULL |
| 1 | 0 | 0 | 0 | 0 | NULL |
| 5 | 0 | 40 | 0 | 1 | 40.0000 |
| 6 | 0 | 80 | 0 | 1 | 80.0000 |
| 8 | 100 | 0 | 89 | 2 | 94.5000 |
+----------+--------+--------+--------+-----------------+-----------------+
9 rows in set (0.00 sec) 【解二结果】
+----------+--------+--------+--------+-----------------+-----------------+
| 学生ID | 语文 | 数学 | 英语 | 有效课程数 | 有效平均分 |
+----------+--------+--------+--------+-----------------+-----------------+
| 9 | NULL | NULL | NULL | 0 | NULL |
| 4 | NULL | NULL | NULL | 0 | NULL |
| 1 | NULL | NULL | NULL | 1 | 13.0000 |
| 7 | NULL | NULL | NULL | 1 | 37.0000 |
| 3 | NULL | NULL | NULL | 2 | 50.5000 |
| 8 | 100 | NULL | 89 | 3 | 63.0000 |
| 5 | NULL | 40 | NULL | 2 | 67.0000 |
| 6 | NULL | 80 | NULL | 1 | 80.0000 |
| 2 | NULL | NULL | NULL | 1 | 89.0000 |
+----------+--------+--------+--------+-----------------+-----------------+
9 rows in set (0.00 sec)

  3、在count中直接用CASE WHEN ESLE END来针对符合特定条件的记录进行计数

  这里直接拿 题目29 来举例——按各科平均成绩从低到高和及格率的百分数从高到低顺序:

【这里优先按平均成绩从低到高排序,若遇到平均成绩相同的则按及格率百分数从高到低排序】
select
course_id as 课程ID,
avg(score) as 平均成绩,
concat(100*count(case when score>=60 then 1 else null end)/count(score),"%") as 及格率
from
score
group by
course_id
order by
avg(score) asc,
count(case when score>=60 then 1 else null end)/count(score) desc;

  查询结果:

+----------+--------------+-----------+
| 课程ID | 平均成绩 | 及格率 |
+----------+--------------+-----------+
| 2 | NULL | NULL |
| 7 | 34.0000 | 33.3333% |
| 8 | 45.0000 | 0.0000% |
| 4 | 60.0000 | 50.0000% |
| 3 | 62.3333 | 33.3333% |
| 6 | 89.0000 | 100.0000% |
| 5 | 100.0000 | 100.0000% |
+----------+--------------+-----------+
7 rows in set (0.00 sec)

★如何在mysql中同时查询显示每个分组的前几名

【参考文章】如何在mysql中查询每个分组的前几名

  1、一种较优雅的方式

  这里直接拿 题目31举例——查询各科成绩前三名的记录(不考虑成绩并列情况):

【注:这里仍然是按照score表默认的排序,即sid的排序】
select
*
from
score
where
(
select
count(*)
from
score as s
where
s.course_id = score.course_id
and
s.score <= score.score
)
<= 3;
+-----+------------+-----------+-------+
| sid | student_id | course_id | score |
+-----+------------+-----------+-------+
| 1 | 1 | 1 | 60 |
| 2 | 1 | 2 | 21 |
| 3 | 2 | 2 | 99 |
| 4 | 3 | 3 | 56 |
| 5 | 4 | 1 | 56 |
| 6 | 5 | 3 | 94 |
| 7 | 5 | 4 | 40 |
| 8 | 6 | 4 | 80 |
| 9 | 7 | 3 | 37 |
| 10 | 8 | 5 | 100 |
| 11 | 8 | 6 | 89 |
| 12 | 8 | 7 | 0 |
| 13 | 3 | 8 | 45 |
| 15 | 2 | 7 | 89 |
| 16 | 2 | 1 | 61 |
+-----+------------+-----------+-------+
15 rows in set (0.00 sec)

  缺点——时间复杂度均为分组中条目数的二次方。很多优化器都不能优化这种查询,使得它的耗时最好为全表行数的二次方(尤其在没有设置正确的索引时),而且数据量大时,可能将服务器会停止响应。那么还有更好的方法吗?有没有办法可以仅仅扫描一次数据,而不是通过子查询进行多次扫描。

  2、一种更高效的方式(使用自定义变量)

  这里直接拿 题目44举例——查询每门课程成绩最好的前两名学生id和姓名

set @num := 0, @cname := '';
select
t2.cid as 课程ID,
t2.cname as 课程名,
t1.sid as 学生ID,
t1.sname as 学生名,
t1.score as 成绩,
@num := if(@cname = t2.cname, @num + 1, 1) as 排名,
@cname := t2.cname as 课程名确认
from
(
select
stu.sid, stu.sname, sco.course_id, sco.score
from
student as stu inner join score as sco
on stu.sid = sco.student_id
) as t1
right join
course as t2
on
t1.course_id = t2.cid
group by
t2.cid, t1.score, t1.sname
having
排名 <= 2;
+----------+--------------------------+----------+-----------------+--------+--------+--------------------------+
| 课程ID | 课程名 | 学生ID | 学生名 | 成绩 | 排名 | 课程名确认 |
+----------+--------------------------+----------+-----------------+--------+--------+--------------------------+
| 1 | 生物 | 4 | 葫芦娃 | 56 | 2 | 生物 |
| 1 | 生物 | 7 | 樱木花道 | 89 | 1 | 生物 |
| 2 | 体育 | 1 | 乔丹 | 21 | 2 | 体育 |
| 2 | 体育 | 2 | 艾弗森 | 99 | 1 | 体育 |
| 3 | 物理 | 7 | 樱木花道 | 37 | 1 | 物理 |
| 3 | 物理 | 3 | 科比 | 56 | 2 | 物理 |
| 4 | 数学 | 5 | 流河旱树 | 40 | 1 | 数学 |
| 4 | 数学 | 6 | 美少女战士 | 80 | 2 | 数学 |
| 5 | 语文 | 8 | 松岛菜菜子 | 100 | 1 | 语文 |
| 6 | 英语 | 8 | 松岛菜菜子 | 89 | 1 | 英语 |
| 7 | 土遁•沙地送葬 | 8 | 松岛菜菜子 | 0 | 2 | 土遁•沙地送葬 |
| 7 | 土遁•沙地送葬 | 2 | 艾弗森 | 89 | 1 | 土遁•沙地送葬 |
| 8 | 夏日喂蚊子大法 | 3 | 科比 | 45 | 1 | 夏日喂蚊子大法 |
| 9 | 麻将牌九扑克千术 | NULL | NULL | NULL | 1 | 麻将牌九扑克千术 |
+----------+--------------------------+----------+-----------------+--------+--------+--------------------------+
14 rows in set (0.00 sec)

  这种查询方法在MySQL中只进行一次扫描,而且没有文件排序(filesort)和临时表(但似乎只支持group by为其中一个连接表的主键?)。

第四模块MySQL50题作业,以及由作业引申出来的一些高端玩法的更多相关文章

  1. 《学习OpenCV》练习题第四章第二题

    #include <highgui.h> #include <cv.h> #pragma comment (lib,"opencv_calib3d231d.lib&q ...

  2. 《学习OpenCV》练习题第四章第一题b&c

    #include <highgui.h> #include <cv.h> #pragma comment (lib,"opencv_calib3d231d.lib&q ...

  3. 《学习OpenCV》练习题第四章第一题a

    #include <highgui.h> #include <cv.h> #pragma comment (lib,"opencv_calib3d231d.lib&q ...

  4. 集美大学1414班软件工程个人作业2——个人作业2:APP案例分析

    一.作业链接 个人作业2:APP案例分析 二.博文要求 通过分析你选中的产品,结合阅读<构建之法>,写一篇随笔,包含下述三个环节的所有要求.  第一部分 调研, 评测 下载软件并使用起来, ...

  5. 【第二次个人作业】结对作业Core第一组:四则运算生成PB16061082+PB16120517

    [整体概况] 1.描述最终的代码的实现思路以及关键代码. 2.结对作业两个人配合的过程和两个人分工. 3.API接口文档和两个组的对接. 4.效能分析,优化分析和心得体会. [代码实现] 一. 实现功 ...

  6. [福大软工] Z班 团队作业——UML设计 作业成绩

    团队作业--UML设计 作业链接 http://www.cnblogs.com/easteast/p/7745703.html 作业要求 1)团队分工(5分) 描述团队的每个成员分别完成了UML图的哪 ...

  7. A.01.10—模块的输出—PWM高端输出

    PWM高端输出比低端输出用得多,如上次提到的卤素灯的控制均是采用高端输出的. PWM高端输出与PWM低端输出的差异就像固态高端输出与固态低端输出的差异类似,从线路失效后对用户的影响来看:高端输出为控制 ...

  8. A.01.02—模块的输入—高端输入

    高端输入即一个高电平信号输入到模块,模块采样时最典型的是采用下拉电阻采样,当然,还有限流电阻和分压电阻,具体可以参见实际电路. 高端输入在汽车上用得不多,这种类型的输入既可以是开关提供的也可以是模块提 ...

  9. 小黄人IP营销的四种玩法思维导图

    小黄人IP营销的四种玩法思维导图 ------------------------------ 本人微信公众帐号: 心禅道(xinchandao) 本人微信公众帐号:双色球预测合买(ssqyuce)

随机推荐

  1. QQ 聊天机器人小薇 1.0.1 发布!

    本次发布主要解决了消息丢失(Api返回码[1202])问题,并改进了改进了一些细节. 简介 XiaoV(小薇)是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动: 监听多个 Q ...

  2. html dl dt dd 标签语法与使用

    一.dl dt dd认识 html <dl> <dt> <dd>是一组合标 […]

  3. 二维数组中的查找(C++和Python实现)

    (说明:本博客中的题目.题目详细说明及参考代码均摘自 “何海涛<剑指Offer:名企面试官精讲典型编程题>2012年”) 题目 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列 ...

  4. c# BackGroundWorker 多线程操作的小例子 (转)

    在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...

  5. MSSQL 备份与恢复

    建立维护计划,需启用<SQL Server 代理>服务 建立三个子作业: 1. 按周进行的全备份,每周日零点执行 2. 按天进行的差异备份,每天中午12点执行 3. 按小时执行的事务日志备 ...

  6. SQL Server ->> 校检函数CHECKSUM、CHECKSUM_AGG、BINARY_CHECKSUM和HASHBYTES

    今天特地查了一下SQL Server下的校检函数有哪些.原本我只是在工作中用过一个CHECKSUM,今天特地学习了一下才发现原来还有其他的校检函数. 这里找到了别人对于SQL SERVER下这几个校检 ...

  7. POI读取xls和xlsx

    import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import  ...

  8. javascript 时间日期处理相加,减操作方法js

    javascript 时间日期处理相加,减操作方法js function dateAddDays(dataStr,dayCount){ var strdate = dataStr; // 2017年0 ...

  9. 根据ip抓 包

    tcpdump -i any -s 0  host 101.81.134.53 -c 1000  -w ./zhj.cap

  10. Python初学者第十一天 文件处理_batch

    11day 文件的操作分为读.写.修改 1.读: f = open(file='D:\新建文本文档.txt',mode='r',encoding='gbk') data = f.read() prin ...