美文网首页
sql练习题

sql练习题

作者: 今年五年级 | 来源:发表于2020-12-23 17:17 被阅读0次

    题目采自网络,解答来自本人/网络,有些题重点在于思路,很简单的关联的字段查询没处理

    –1.学生表
    Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别
    –2.课程表
    Course(c_id,c_name,t_id) – --课程编号, 课程名称, 教师编号
    –3.教师表
    Teacher(t_id,t_name) --教师编号,教师姓名
    –4.成绩表
    Score(s_id,c_id,s_score) --学生编号,课程编号,分数

    测试数据

    --建表
    --学生表
    CREATE TABLE `Student`(
        `s_id` VARCHAR(20),
        `s_name` VARCHAR(20) NOT NULL DEFAULT '',
        `s_birth` VARCHAR(20) NOT NULL DEFAULT '',
        `s_sex` VARCHAR(10) NOT NULL DEFAULT '',
        PRIMARY KEY(`s_id`)
    );
    --课程表
    CREATE TABLE `Course`(
        `c_id`  VARCHAR(20),
        `c_name` VARCHAR(20) NOT NULL DEFAULT '',
        `t_id` VARCHAR(20) NOT NULL,
        PRIMARY KEY(`c_id`)
    );
    --教师表
    CREATE TABLE `Teacher`(
        `t_id` VARCHAR(20),
        `t_name` VARCHAR(20) NOT NULL DEFAULT '',
        PRIMARY KEY(`t_id`)
    );
    --成绩表
    CREATE TABLE `Score`(
        `s_id` VARCHAR(20),
        `c_id`  VARCHAR(20),
        `s_score` INT(3),
        PRIMARY KEY(`s_id`,`c_id`)
    );
    --插入学生表测试数据
    insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
    insert into Student values('02' , '钱电' , '1990-12-21' , '男');
    insert into Student values('03' , '孙风' , '1990-05-20' , '男');
    insert into Student values('04' , '李云' , '1990-08-06' , '男');
    insert into Student values('05' , '周梅' , '1991-12-01' , '女');
    insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
    insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
    insert into Student values('08' , '王菊' , '1990-01-20' , '女');
    --课程表测试数据
    insert into Course values('01' , '语文' , '02');
    insert into Course values('02' , '数学' , '01');
    insert into Course values('03' , '英语' , '03');
    
    --教师表测试数据
    insert into Teacher values('01' , '张三');
    insert into Teacher values('02' , '李四');
    insert into Teacher values('03' , '王五');
    
    --成绩表测试数据
    insert into Score values('01' , '01' , 80);
    insert into Score values('01' , '02' , 90);
    insert into Score values('01' , '03' , 99);
    insert into Score values('02' , '01' , 70);
    insert into Score values('02' , '02' , 60);
    insert into Score values('02' , '03' , 80);
    insert into Score values('03' , '01' , 80);
    insert into Score values('03' , '02' , 80);
    insert into Score values('03' , '03' , 80);
    insert into Score values('04' , '01' , 50);
    insert into Score values('04' , '02' , 30);
    insert into Score values('04' , '03' , 20);
    insert into Score values('05' , '01' , 76);
    insert into Score values('05' , '02' , 87);
    insert into Score values('06' , '01' , 31);
    insert into Score values('06' , '03' , 34);
    insert into Score values('07' , '02' , 89);
    insert into Score values('07' , '03' , 98);
    

    题目与作答

          -- 1.count(1)与count(*)得到的结果一致,包含null值,*包括了id字段,每行必有一个不空的字段。
          -- 2.count(字段)不计算null值
          -- 3.count(null)结果恒为0  
    
    -- 1 查询"01"课程比"02"课程成绩高的学生的信息及课程分数  要点:必须2个都有成绩,否则01成绩有的学生有,02这个学生没有就会直接忽略
    select * from score a inner join score b on a.s_id=b.s_id where a.c_id ='01' and b.c_id='02' and a.s_score>b.s_score
    
    -- 第二种写法,不算where部分的话也是笛卡尔积
    SELECT
        * 
    FROM
        ( SELECT s_id, s_score FROM score WHERE c_id = '01' ) t1,
        ( SELECT s_id, s_score FROM score WHERE c_id = '02' ) t2 
    WHERE
        t1.s_id = t2.s_id 
        AND t1.s_score > t2.s_score
        
    -- 1.1 查询不存在01课程但是存在02课程的情况
    select s_id from score where c_id='02' and s_id not in(select s_id from score where c_id='01')
    
    -- 2 查询几门课平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩,round保留一位小数,会四舍五入
    select s_id,round(avg(s_score),1) avg_score from score group by s_id having avg_score>=60
    
    -- 3 查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩(包括有成绩的和无成绩的) ,先group by,然后ifnull判断为0处理0结果,最后再having排除
    select a.s_id,round(avg(IFNULL(b.s_score,0)),1)as avgScore from student a left join score b on a.s_id=b.s_id group by a.s_id having avgScore<60
    
    -- 4 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩,包括没有课程的   注意:必须是全部学生表的学生id字段,成绩表的id字段缺8号学生
    select a.s_id,count(b.c_id),sum(IFNULL(b.s_score,0)) from student a left join score b on a.s_id =b.s_id group by a.s_id;
    
    -- 易错点:b表score表中无8号学生数据,如果用student表和score表连接,直接获取score表的学生id将遗漏
    select * from student a left join score b on a.s_id=b.s_id;
    
    select b.s_id,count(b.c_id),sum(b.s_score) from student a left join score b on a.s_id =b.s_id group by b.s_id  -- (3)
    -- 第一个语句,最理想化的模型
    -- 第二个语句,因为左连接导致将8号学生在group by 之前大表左边算上了,但是group by的时候a表的8号学生有,b表的8号学生没有,(3)的SQL执行结果务必注意
    
    -- 5 查询学过"张三"老师授课的同学的信息
    select c.s_id from teacher a inner join course b on a.t_id=b.t_id left join score c on b.c_id=c.c_id where a.t_name='张三' group by c.s_id
    SELECT s_id FROM score WHERE c_id =( SELECT c_id FROM teacher a INNER JOIN course b ON a.t_id = b.t_id WHERE a.t_name = '张三' )
    
    -- 6 查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息
    select a.s_id from score a inner join score b on a.s_id=b.s_id where a.c_id='01' and b.c_id='02'
    select a.s_id from score a,score b where a.s_id=b.s_id and a.c_id='01' and b.c_id='02'
    
    -- 7 查询没有学全所有课程的同学的信息
    SELECT * FROM student WHERE s_id NOT IN ( SELECT s_id FROM score GROUP BY s_id HAVING count( c_id ) = ( SELECT count( c_id ) FROM course ) )
    
    -- 8 查询和"01"号的同学学习的课程完全相同的其他同学的信息   ***********
    -- 用这个group_concat结果去左连接
    SELECT s_id FROM
        ( SELECT GROUP_CONCAT( c_id ) subjects FROM score WHERE s_id = 01 GROUP BY s_id ) a
        INNER JOIN ( SELECT s_id, GROUP_CONCAT( c_id ) subjects FROM score GROUP BY s_id ) b ON a.subjects = b.subjects 
    WHERE
        s_id != '01'
        
    -- 9 查询没学过"张三"老师讲授的任一门课程的学生姓名
    SELECT s_name FROM student WHERE s_id NOT IN (SELECT s_id FROM score WHERE c_id IN ( SELECT c_id FROM course WHERE t_id = ( SELECT t_id FROM teacher WHERE t_name = '张三' )))
    
    -- 10 查询不及格课程有两门及两门以上的同学的学号,姓名及其平均成绩
    SELECT
        a.s_id,
        ( SELECT s_name FROM student WHERE s_id = a.s_id ),
        round( avg( a.s_score ), 1 ) 
    FROM
        score a
        INNER JOIN (
        SELECT
            s_id,
            sum( CASE WHEN s_score < 60 THEN 1 ELSE 0 END ) counts 
        FROM
            score 
        GROUP BY
            s_id 
        HAVING
            counts >= 2 
        ) b ON a.s_id = b.s_id 
    GROUP BY
        a.s_id
        
    -- 11 按照平均成绩从高到低显示【所有学生】的所有课程的成绩以及平均成绩,直接用avg解法有误,avg默认会不包括null,即null,89,null的品均分为89大于80,80,80的平均分为80不合理
    SELECT
        a.s_id,
        ( SELECT s_score FROM score WHERE s_id = a.s_id AND c_id = '01' )语文,
        ( SELECT s_score FROM score WHERE s_id = a.s_id AND c_id = '02' )数学,
        ( SELECT s_score FROM score WHERE s_id = a.s_id AND c_id = '03' )英语,
        round(sum(s_score)/3,2) avgScore     -- 这里如果理解为选修几门课程的话,而不是必选的话,这里用avg函数是合理的 avg(s_score)
    FROM
        student a
        LEFT JOIN score b ON a.s_id = b.s_id 
    GROUP BY
        a.s_id 
    ORDER BY
        avgScore DESC;
        
    -- 12 查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
    -- 及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 
    -- 注意:该科目的成绩可能某一栏位是空的,空的不能算!!!因此不能直接除以count(*),而应该用case when或者ifnull
    SELECT
        a.c_id,
        ( SELECT c_name FROM course WHERE c_id = a.c_id ) 课程名称,
        max( s_score ) 最高分,
        min( s_score ) 最低分,
        round( avg( s_score ), 1 ) 平均分,
        round( sum( CASE WHEN s_score >= 60 THEN 1 ELSE 0 END ) / sum(case when s_score is not null then 1 else 0 end), 2 ) 及格率,
        round( sum( CASE WHEN s_score >= 70 AND s_score < 80 THEN 1 ELSE 0 END ) / sum(case when s_score is not null then 1 else 0 end), 2 ) 中等率,
        round( sum( CASE WHEN s_score >= 80 AND s_score < 90 THEN 1 ELSE 0 END ) / sum(case when s_score is not null then 1 else 0 end), 2 ) 优良率,
        round( sum( CASE WHEN s_score >= 90 THEN 1 ELSE 0 END ) / sum(case when s_score is not null then 1 else 0 end), 2 ) 优秀率
    FROM
        score a 
    GROUP BY
        c_id;
        
    -- 13 按各科成绩进行排序,并显示排名,mysql没有rank函数  
                -- 声明变量@方式API DOC https://blog.csdn.net/justry_deng/article/details/80597916
                -- 排名跳级版本:age升序排列(score相同时,排名相同;但是到下一个score不同时,排名跳级+n)
    SELECT c_id,s_id,s_score,
        @inRank := @inRank + 1,
        @curRank :=IF( @prevScore = s_score, @curRank, @inRank ) AS rank,   -- 写法2 : @curRank:=case when @prevScore=s_score then @curRank else @inRank end
        @prevScore := s_score
    FROM
        score a,( SELECT @curRank := 0, @prevScore := NULL, @inRank := 0 ) b 
    ORDER BY c_id ASC,s_score DESC
        
        -- 每个科目分开排名版本
        SELECT c_id,s_id,s_score,
        @inRank := if(@prevSubject=c_id,@inRank+1,1),
        @curRank :=IF( @prevScore = s_score, @curRank, @inRank ) AS rank,   -- 这一步保证跳的时候,正确赋值给curBank
        @prevScore := s_score ,
        @prevSubject:=c_id
    FROM
        score a,( SELECT @curRank := 0, @prevScore := NULL,@prevSubject:=null, @inRank := 0 ) b 
    ORDER BY c_id ASC,s_score DESC
    
    -- 解法2 自交  -- 首先左边所有记录挨个对应一遍右边第一列,然后左边所有记录挨个对应一遍右边第二列...左边6列*右边每一列对应6次=36条
    select * from score a left join score b on a.c_id=b.c_id where a.c_id='01';
    -- 丢弃左边大于等于右边的记录,右边从1到6条记录,没跳对应左边的都小于右边
    select * from score a left join score b on a.c_id=b.c_id where a.c_id='01' and a.s_score<b.s_score;
    -- 应该group by 的是a表,group by b表没有意义,因为要对每一条记录进行排名,是a的记录,算的是group by以后比a大的记录个数
    SELECT
        a.c_id,
        a.s_id,
        a.s_score,
        count( b.s_score )+1 AS bigCountPlus1 
    FROM
        score a
        LEFT JOIN score b ON a.c_id = b.c_id and a.s_score < b.s_score 
    GROUP BY
        a.c_id,
        a.s_id,
        a.s_score 
    ORDER BY
        a.c_id,
        bigCountPlus1   
        
    -- 14 查询学生的总成绩并进行排名--成绩相同需要跳参考上面,这边不再处理
    select a.*,@inRank:=@inRank+1,@rank:=if(@preScore=sumScore,@rank,@inRank),@preScore:=sumScore as rank from (
        select s_id,sum(s_score) sumScore from score group by s_id order by sumScore desc
    ) a,(select @rank:=0,@inRank:=0,@preScore:=null)b
    
    -- 15 查询不同老师所教不同课程平均分从高到低显示 
    select a.t_id,a.t_name,b.c_id,b.c_name,avg(c.s_score) avgScore from teacher a inner join course b on a.t_id=b.t_id left join score c on b.c_id=c.c_id group by t_id,t_name,c_id,c_name order by avgScore desc
    
    -- 16 查询所有课程的成绩第2名到第3名的学生信息及该课程成绩  课程1前两名并列第一,第三名就是第三名,不存在第二名
    select c.s_id,c.s_score,c.c_id,c.rank from(
    select a.s_id,a.s_score,a.c_id,@inRank:=if(@prevSubject=a.c_id,@inRank+1,1),@rank:=if(@preScore=a.s_score,@rank,@inRank) as rank,@preScore:=a.s_score,@prevSubject:=a.c_id from score a,(select @rank:=0,@inRank:=0,@preScore:=null,@prevSubject:=null)b order by a.c_id,a.s_score desc
    )c where c.rank in(2,3) order by c.c_id ;
    
    -- 17 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[0-60]及所占百分比
    select main.*,
        round(main.`85-100`/(select count(*) from score where c_id=main.c_id),2)as "85-100占比",
        round(main.`70-85`/(select count(*) from score where c_id=main.c_id),2)as "70-85占比",
        round(main.`60-70`/(select count(*) from score where c_id=main.c_id),2)as "60-70占比",
        round(main.`0-60`/(select count(*) from score where c_id=main.c_id),2)as "0-60占比"
    from (
        select c_id,
        sum(if(s_score>85 and s_score<=100,1,0)) as "85-100",
        sum(if(s_score>70 and s_score<=85,1,0)) as "70-85",
        sum(if(s_score>60 and s_score<=70,1,0)) as "60-70",
        sum(if(s_score>=0 and s_score<=60,1,0)) as "0-60"
        from score group by c_id
    )main
    
    -- 18 获取各科成绩前3名的记录 
        -- 参照第13题作答
    
    -- 19 查询所有学生的课程及分数情况
    select a.s_id,
        sum(case when c_name='语文' then s_score else 0 end)as '语文',
        sum(case when c_name='数学' then s_score else 0 end)as '数学',
        sum(case when c_name='英语' then s_score else 0 end)as '英语',
        sum(s_score) as '总分'
    from score a left join course b on a.c_id=b.c_id group by a.s_id
    
    -- 20 查询选修"张三"老师所授课程的学生中,成绩最高的学生信息及其成绩  考虑并列第一的情况
    SELECT b.* FROM score a left join student b on a.s_id=b.s_id
    WHERE a.s_score = (SELECT max( s_score ) AS topScore FROM score WHERE c_id IN ( SELECT c_id FROM course WHERE t_id IN 
    ( SELECT t_id FROM teacher WHERE t_name = '张三' ) ) ) 
    AND a.c_id = ( SELECT c_id FROM course WHERE t_id IN ( SELECT t_id FROM teacher WHERE t_name = '张三' ) )
    
    -- 21 查询不同课程成绩相同的学生编号,课程编号,学生成绩
    select DISTINCT b.s_id,b.c_id,b.s_score from score a,score b where a.c_id != b.c_id and a.s_score = b.s_score
    
    -- 22 查询每门课成绩最好的前两名
        -- 解法1 参考13 解法2
        -- 解法2
    select a.s_id,a.c_id,a.s_score from score a
            where (select COUNT(1) from score b where b.c_id=a.c_id and b.s_score>=a.s_score)<=2 ORDER BY a.c_id
    
    -- 23 统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列  
    select c_id,count(s_id) counts from score group by c_id having counts>5 order by counts desc,c_id asc
    
    -- 24 查询各学生的年龄
    SELECT TIMESTAMPDIFF(YEAR,s_birth,DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%S')) from student
    -- 解法2
                select s_birth,(DATE_FORMAT(NOW(),'%Y')-DATE_FORMAT(s_birth,'%Y') - 
                    (case when DATE_FORMAT(NOW(),'%m%d')>DATE_FORMAT(s_birth,'%m%d') then 0 else 1 end)) as age
            from student;
            
    -- 25 查询本周过生日的学生
    select * from student where WEEK(DATE_FORMAT(NOW(),'%Y-%m-%d'))=WEEK(s_birth);
    
    -- 26 查询下月过生日的学生
    select * from student where month(date_format(now(),'%Y-%m-%d'))+1=month(s_birth)
    

    相关文章

      网友评论

          本文标题:sql练习题

          本文链接:https://www.haomeiwen.com/subject/vklunktx.html