• 第四模块MySQL50题作业,以及由作业引申出来的一些高端玩法


    一、表关系

    先参照如下表结构创建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 = '1' THEN  population ELSE 0 END) as '',
        SUM( CASE WHEN sex = '2' 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为其中一个连接表的主键?)。

  • 相关阅读:
    华为防火墙server-map、ALG
    华为防火墙来回路径不一致、智能选路随笔
    锐捷交换机修改openssl秘钥加密算法
    华为交换机板卡类型
    H3C交换机堆叠口、MAD检测口
    内网用户使用公网IP访问内部服务
    记一次SSL证书排错
    解析teambition文件为分享链接
    tomcat启动乱码如何解决?
    BOM与DOM的区别与联系
  • 原文地址:https://www.cnblogs.com/oceanicstar/p/9160177.html
Copyright © 2020-2023  润新知