SQL面试50题

SQL面试50题

0-SQL面试表构建

一、表结构和关联关系

二、建表和插入测试数据

1.查询课程编号为''01''的课程比''02''的课程成绩高的所有学生的学号(重点)。

2.查询平均成绩大于60分的学生学号和平均成绩

3.查询所有学生的学号、姓名、选课数、总成绩

4.查询姓侯的老师的个数

5.查询没学过张三老师课的学生学号、姓名(重点)

6.查询学过张三老师所教的所有课程的同学的学号、姓名(重点)

7.查询学过编号为01的课程并且也学过编号为‘02’的课程的学生的学号、姓名(重点)

8.查询课程编号为'02'的总成绩(不重点)

9.查询所有课程成绩小于60分的学生的学号、姓名;

10.查询没有学全所有课的学生的学号、姓名(重点)

11.查询至少有一门课与学号为'01'的学生所学课程相同的学生的学号和姓名(重点)

12.查询和'01'号同学所学课程完全相同的其他同学的学号(重点)

13.查询没学过‘张三’老师教授的任一门课程的学生姓名和47题一样(重点,能做出来)

15.查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)

16.检索‘01’课程分数小于60,按分数降序排列的学生信息;

17.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重点)

18.查询各科成绩最高分、最低分和平均分:以如下形式显示:

19.按各科成绩进行排序,并显示排名(重点 row_number)

20.查询学生的总成绩并进行排名

21.查询不同老师所教不同课程平均分从高到低显示;

22.查询所有课程的成绩第2名到第3名的学生信息及该课程成绩

23.使用分段【100-85)、【85-70)、【70-60)、【<60】来统计各科成绩,分别统计各分数段人数:课程ID和课程名称;(重点)

24.查询学生平均成绩及其名次

26.查询每门课被选修的学生数

27.查询出只有两门课程的全部学生的学号和姓名;

28.查询男生、女生人数

29.查询名字中含有“风”字的学生信息

31.查询1990 年出生的学生名单(重点)

32.查询平均成绩大于等于85分的所有学生的学号、姓名、平均成绩

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

34.查询课程名称为‘数学’,且分数低于60的学生姓名和分数;

35、查询所有学生的课程及分数情况(重点)

36.查询课程成绩在70分以上课程名称,分数和学生姓名;

37.查询不及格的课程并按课程号从大到小排列

38、查询课程编号为03且课程成绩在80分以上的学生的学号和姓名(不重要)

39、求每门课程的学生人数(不重要)

40、查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top)

41.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)

43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列(不重要)

44、检索至少选修两门课程的学生学号(不重要)

45、 查询选修了全部课程的学生信息(重点划红线地方)

46、查询各学生的年龄(精确到月份)

47、查询没学过“张三”老师讲授的任一门课程的学生姓名(还可以,自己写的,答案中没有)

49.查询本月过生日的人

50.查询下个月过生日的同学

 


 

0-SQL面试表构建

一、表结构和关联关系

二、建表和插入测试数据

(1)建表语句:

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`)
) CHARSET = utf8;


CREATE TABLE `Course`
(
    `c_id`   VARCHAR(20),
    `c_name` VARCHAR(20) NOT NULL DEFAULT '',
    `t_id`   VARCHAR(20) NOT NULL,
    PRIMARY KEY (`c_id`)
) CHARSET = utf8;


CREATE TABLE `Teacher`
(
    `t_id`   VARCHAR(20),
    `t_name` VARCHAR(20) NOT NULL DEFAULT '',
    PRIMARY KEY (`t_id`)
) CHARSET = utf8;


CREATE TABLE `Score`
(
    `s_id`    VARCHAR(20),
    `c_id`    VARCHAR(20),
    `s_score` INT(3),
    PRIMARY KEY (`s_id`, `c_id`)
) CHARSET = utf8;

在建立每张表的时候,需要设置CHARSET=utf8,否则出现中文字符时会报错;

>ERROR 1366 (HY000): Incorrect string value: '\xE8\xB5\xB5\xE9\x9B\xB7' for column 'Sname' at row 1

解决方案:分别查看数据库、数据表、报错列字段的字符集,错误就是由于三者不一致造成的,设置一致;一般为了支持中文,设置为utf8;

最后总结:(提示:以下内容都需要首先进入Mysql客户端)

  • 查看mysql的字符集:show variables where Variable_name like '%char%';
  • 查看某一个数据库字符集:show create database test;(注:test为数据库)
  • 查看某一个数据表字符集:show create table Student;(注:Student为数据表)

修改mysql的字符集:

  • mysql> set character_set_client=utf8;
  • mysql> set character_set_connection=utf8;
  • mysql> set character_set_database=utf8;
  • mysql> set character_set_results=utf8;
  • mysql> set character_set_server=utf8;
  • mysql> set character_set_system=utf8;
  • mysql> set collation_connection=utf8;

修改数据库的字符集:

     alter database test character set utf8;

修改数据表的字符集:

      alter table Student character set utf8;

修改数据表字段的字符集:

     alter table Student change Sname Sname varchar(10) character set utf-8;


1.查询课程编号为''01''的课程比''02''的课程成绩高的所有学生的学号(重点)。

select s1.s_id
from Score s1,
     Score s2
where s1.s_id = s2.s_id
  and s1.c_id = '01'
  and s2.c_id = '02'
  and s1.s_score > s2.s_score;

思路:我想要一张什么样的表。

s_id

c1_score

c2_score

学生id

课程c1的成绩

课程c2的成绩

它解:

select a.s_id 's_no', c.s_name 'name', a.s_score '01', b.s_score '02'
from (select s_id, c_id, s_score from Score where c_id = '01') as a
         inner join (select s_id, c_id, s_score from Score where c_id = '02') as b on a.s_id = b.s_id
         inner join (select s_name, s_id from Student) as c on a.s_id = c.s_id
where a.s_score > b.s_score

总结:自解中直接将两张原始表通过学生学号字段进行链接,笛卡尔积更大,计算量越大,效率越低。在它解中,首先分别查出01编号和02编号的表信息,然后再根据学生id字段进行链接,减少了无意义的链接,减少了计算量。因此,在以后进行表链接前,先思考能否在链接前进行一定的筛选再链接。

关键思想:构建子查询;


2.查询平均成绩大于60分的学生学号和平均成绩


select s_id, avg(s_score) 'avg_score'
from Score
group by s_id
having avg(s_score) > 60

建议:在使用group by的时候,尽量不要将group by中没有使用到的字段添加到select中去;即使成绩不报错,这个select也是没有意义的。

使用group by的时候,使用having进行分组的筛选。


3.查询所有学生的学号、姓名、选课数、总成绩

select a.s_id, a.s_name, count(c_id), sum(s_score)
from Student a,
     Score b
where a.s_id = b.s_id
group by a.s_id

思路:

1.做统计,group by

2.关联两张表,构建学号、姓名、课程编号、这门课的成绩

3.考虑将未选课的学生信息也拿出来,因此我们使用学生表左链接成绩表,即显示所有学生表信息,成绩表中不存在的关联信息,显示为null;

它解:

select a.s_id, a.s_name, count(b.c_is), sum(s_score)
from Student as a
         left join Score as b on a.s_id = b.s_id
group by s_id

疑问:

1.join on之后s_id只有一行了吗?group by后面的s_id字段是哪个表的s_id字段?

2.一般不select,group by未出现的统计字段。因此,我们将s_name也加入统计字段,结果不改变,但是更加严谨。

3.一般只有在group by中使用到的字段,我们才会放到select中进行查询。

它解:
 

select a.s_id, a.s_name, count(b.c_is), sum(s_score)
from Student as a
         left join Score as b on a.s_id = b.s_id
group by s_id, a.s_name

改进:不希望成绩列出现null值;加上case when 当成绩列出现NULL值时,设置为0,其他时候就是成绩本身;

select a.s_id, a.s_name, count(b.c_id), sum(case when b.s_score is NULL then 0 else b.s_score END)
from Student as a
         left join Score as b on a.s_id = b.s_id
group by s_id, a.s_name

总结:求和sum中用到case when是很常见的!


4.查询姓侯的老师的个数

select count(t_id)
from Teacher
where t_name like '候%'

注意:

姓张的老师名字: 张%

包含张这个字的老师名字: %张%

不同的老师名字: count(distinct t_name)


5.查询没学过张三老师课的学生学号、姓名(重点)

查询张山老师课程编号

select c.c_id
from Course c,
     Teacher t
where c.t_id = t.t_id
  and t.t_name = '张三'

查询course表,按照c_id分类,查找出所有选该课程的s_id

select s_id
from Score,
     (select c.c_id
      from Course c,
           Teacher t
      where c.t_id = t.t_id
        and t.t_name = '张三') as a

group by a.c_id

(⚠️:不能查询未group by的字段)

自解:

select s_id, s_name
from Student
where s_id not in (select s1.s_id
                   from Score s1
                            inner join Student s2 on s1.s_id = s2.s_id,
                        (select c.c_id
                         from Course c,
                              Teacher t
                         where c.t_id = t.t_id
                           and t.t_name = '张三') as a
                   where a.c_id = s1.c_id)

它解:找出学过张三老师课程的同学,然后排除掉这些人,剩下就是没学过张三老师课程的人。

  1. 找出张三老师的教师编号
  2. 根据教师编号,我们去课程表里面找出张三老师所教课程的课程id
  3. 根据张三老师所教课程的课程id,我们去成绩表表中,找出所有考了该课程的学生id
  4. 根据选该课的学生id,我们查询学生表中学生id not in选课学生id的学生信息。
select s_id, s_name
from Student
where s_id not in (select s_id
                   from Score
                   where c_id = (select c_id from Course where t_id = (select t_id from Teacher where t_name = '张三')))

另解:通过表的关联来进行求解;

select s_id, s_name
from student
where s_id not in (select s_id
                   from Score s
                            inner join Course c on s.c_id = c.c_id
                            inner join Teacher t on c.t_id = t.t_id
                   where t.t_name = '张三')

注意:不能去成绩表中选择 课程号!='02',没有不代表不等于,因为这个同学选择了其他课程也会被选择出来;


6.查询学过张三老师所教的所有课程的同学的学号、姓名(重点)

自解:

select s1.s_id, s1.s_name
from Student s1
         inner join Score s2 on s1.s_id = s2.s_id
         inner join Course c on c.c_id = s2.c_id
         inner join Teacher t on c.t_id = t.t_id
where t.t_name = '张三'

它解:


7.查询学过编号为01的课程并且也学过编号为‘02’的课程的学生的学号、姓名(重点)

自解:

select a.s_id, s.s_name
from Student s,
     (select s_id from Score where c_id = '01') as a
         inner join (select s_id from Score where c_id = '02') as b on a.s_id = b.s_id
where s.s_id = a.s_id

它解:

select s_id, s_name
from Student
where s_id in (select a.s_id
               from (select s_id from Score where c_id = '01') as a (select s_id from Score where c_id='02')as b
               on a.s_id=b.s_id)

优化:最常用的方法就是子查询用零时表来代替;

通过select length(Null),求Null值的长度;


8.查询课程编号为'02'的总成绩(不重点)

自解:

select sum(s_score), avg(s_score), count(s_score), count(distinct s_id) as total_score_02
from Score
where c_id = '02'

sum,avg,count函数的使用;
它解:

select c_id, sum(s_score), avg(s_score), count(s_score), count(distinct s_id) as total_score_02
from Score
group by c_id
having c_id = '02'


9.查询所有课程成绩小于60分的学生的学号、姓名;

自解:

select s.s_id, s.s_name
from Student s
where s.s_id in (select s_id from Score group by s_id having (max(s_score < 60))) 

注意:等号和in的区别,子查询的s_id查询出来是一个列表,前面的是s_id 是in后面的查询结果;

它解:成绩小于60分的课程数和这个学生选的总课程数相等,即该生选的所有课程都小于60分;

select a.s_id, t.s_name
from (select s_id, count(c_id) as cnt from Score where s_score < 60 group by s_id) as a
         inner join (select s_id, count(c_id) as cnt from Score group by s_id) as b on a.s_id = b.s_id
         inner join Student as t on a.s_id = t.s_id
where a.cnt = b.cnt

10.查询没有学全所有课的学生的学号、姓名(重点)

自解1:只要在学生表中查找,学生id不在选课数等于课程数的学生id列表中就可以了

select s_id, s_name
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))

自解2:错误,子查询没有考虑未选课的学生;

select s_id, s_name
from Student
where s_id in (select s_id from Score group by s_id having (count(c_id)) != (select count(c_id) from Course))

注意:万一该同学补考了,会存在一门课对应多个成绩的情况,因此最好加上distinct;

select s_id
from Score
group by s_id
having count(distinct c_id) < 3

它解:错误,漏掉了一门课都没有选的人!

select s_id, s_name
from Student
where s_id in
      (select s_id from Score group by s_id having count(distinct c_id) < (select count(distinct c_id) from Course))

问题:没有考虑没有选课的学生!

它解:采用左链接,左边的数据永远都会出现。若链接的右边数据不存在,则左边的数据依然会出现,右边的数据就为Null select from Student as st left join Score as sc on st.s_id=sc.s_id

拓展:在b表中插入a表中没有的数据;

insert into b
    (select *
     from a
              left join b on a.id = b.id
     where b.id is null
    )

11.查询至少有一门课与学号为'01'的学生所学课程相同的学生的学号和姓名(重点)

自解:

select s_id, s_name
from Student
where s_id in
      (select distinct s_id from Score where c_id in (select c_id from Score where s_id = '01') and s_id != '01')

首先查看'01'学生选了哪些课程

它解:

inner join
select a.s_id, a.s_name
from Student as a
         inner join(select distinct s_id
                    from Score
                    where c_id in (select c_id from Score where s_id = '01') and s_id != '01') as b on a.s_id = b.s_id

推荐:在表的数据较大的时候,建议用inner join,而不使用in。实际生产中更常使用left join和inner join;join比in效率更高;

 


12.查询和'01'号同学所学课程完全相同的其他同学的学号(重点)

自解:使用count ❌ 只选出了课程数与01同学所修课程相同的同学,没考虑课程不同但数目相同的情况;

select s_id
from Score
group by s_id
having count(distinct c_id) = (select count(distinct c_id) from Score where s_id = '01')

注意:having后面不能直接跟字段,having与where的区别!

自解:❌ in只选出了至少有一门课程与01同学所修课程相同的同学;

select distinct s_id
from Score
where s_id in (select s_id
               from (select s_id
                     from Score
                     group by s_id
                     having count(distinct c_id) =
                            (select count(distinct c_id) as a from Score where s_id = '01')) as a)
  and c_id in (select c_id from Score where s_id = '01')
  and s_id != '01'

1.选出学了'01'同学课程列表中课程的学生记录

2.然后计算同学的选课数目是否为3

它解:❌ 存在问题:in 只要有一个in就会被筛选出来,所有第一层过滤出来的是学了01同学所修课程中任意一门课程的同学, 后面再过滤课程数相同,那么同学2就会被选出来。但是,很明显同学2并不满足条件。

select s_id
from Score
where c_id in (select c_id from Score where s_id = '01 )
and s_id!='01' group by s_id having count(distinct c_id) =(select count(distinct c_id) from Score where s_id='01')

 

同学1

同学2

同学3

同学4

a

a

a

a

b

b

b

b

b

d

c

c

   

d

上面,只有同学3和同学1修了完全相同的课程,同学2课程类别不完全相同,同学4课程数目不同;正确解法:☑️ -- 查找出与01同学选课数目相同的同学

-- 查找出与01同学所修课程不完全相同的同学 
select *
from Student
where s_id in (select s_id
               from Score
               group by s_id
               having count(distinct c_id) = (select count(distinct c_id) from Score where s_id = ''01'')
                  and s_id != ''01'')
  and s_id not in (select s_id from Score where c_id not in (select c_id from Score where s_id = ''01''))

01号同学学了01,02,03号课程

1.选出所学的课不在(01,02,03)的同学 -排除;

2.剩下的同学肯定选了 01,02,03中的某几门课,判断所学的课程数是否等于3;

 


13.查询没学过‘张三’老师教授的任一门课程的学生姓名和47题一样(重点,能做出来)

 


15.查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)

问题:select count(s_score<60) from Score为什么统计处了所有的记录,判断条件为什么无效?

答:select count(0) 答案居然是1,卧槽!count(null)时返回的是0 count(非null)返回是1,0就是非null,卧槽!

自解:

select s.s_id, s.s_name, b.avg_score
from Student s
         inner join (select s_id, avg(s_score) avg_score
                     from Score
                     group by s_id
                     having count(case when s_score < 60 then 1 else null end) >= 2) as b on s.s_id = b.s_id


它解:先查询出所有不及格成绩的记录,然后对这些记录按照学号进行分组,筛选出组内学号大于等于2的学号 即有两门及两门以上课程不及格的同学。

select a.s_id, a.s_name, b.avg_score
from Student as a
         inner join (select s_id, avg(s_score) avg_score
                     from Score
                     where s_score < 60
                     group by s_id
                     having count(distinct c_id) >= 2) as b on a.s_id = b.s_id

注意:先筛选,再分组,再筛选;比直接分组筛选效率更高;

select a.s_id, a.s_name, avg(s_score)
from Student as a
         inner join Score as b on a.s_id = b.s_id and a.s_id in (select s_id
                                                                 from Score
                                                                 where s_score < 60
                                                                 group by s_id
                                                                 having count(distinct c_id) >= 2)
group by a.s_id, a.s_name

思路:首先找出有两门课程成绩不及格的学生学号,然后将学生表和成绩表连接起来,进行筛选;

 


16.检索‘01’课程分数小于60,按分数降序排列的学生信息;

自解:

select *
from Student as a
where a.s_id in (select s_id from Score where c_id = '01' and s_score < 60 order by s_score desc)

它解:采用inner join

select t.*, s.c_id, s.s_score
from Student t
         inner join Score s on t.s_id = s.s_id
where s.c_id = '01'
  and s.s_score < 60
order by s_score desc

降序 desc 升序 asc

注意在使用innner join时理清楚表之间的关系,是一对多还是多对多等等;


17.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重点)

自解:

select s.s_id, s.c_id, s.s_score, b.avg_score
from Score s,
     (select s_id, avg(s_score) avg_score from Score group by s_id) as b
where s.s_id = b.s_id
order by b.avg_score desc, s_id

它解:问题:显示结果不佳;

select s.s_id, s.c_id, s.s_score, b.avg_s_score
from Score as s
         inner join (select s_id, avg(s_score) as avg_s_score from Score group by s_id) as b on s.s_id = b.s_id
order by b.avg_s_score desc

理想显示结果:

学生学号

语文

数学

英语

平均成绩

01

80

98

97

xx

它解:理想显示结果

select s_id                                                  '学号',
       Max(case when c_id = '01' then s_score else Null end) '语文',
       Max(case when c_id = '02' then s_score else Null end) '数学',
       Max(case when c_id = '03' then s_score else Null end) '英语',
       Max(case when c_id = '04' then s_score else Null end) '化学',
       avg(s_score)
from Score
group by s_id
order by avg(s_score) desc

注意:select 只能查询group by出现的或者统计函数出现的字段,因此在这里我们使用max函数构造统计函数字段,max函数其实没有用处,只是为了能够取到各门课程的成绩值。这里使用min,avg,sum等函数取值也是可以的,因为这里一个人一门课程只有一个成绩。


18.查询各科成绩最高分、最低分和平均分:以如下形式显示:

课程ID、课程name、最高分、最低分、平均分、及格率、中等率、优良率、优秀率

--及格为>=60、中等为:70-80、优良为80-90、优秀为>=90

自解:

select c.c_id                                                                           '课程ID',
       c_name                                                                           '课程name',
       max(s_score)                                                                     '最高分',
       min(s_score)                                                                     '最低分',
       avg(s_score)                                                                     '平均分',
       count(case when s_score >= 60 then 0 else null end) / count(*)                   '及格率',
       count(case when s_score >= 70 and s_score <= 80 then 0 else null end) / count(*) '中等率',
       count(case when s_score >= 80 and s_score <= 90 then 0 else null end) / count(*) '优良率',
       count(case when s_score >= 90 then 0 else null end) / count(*)                   '优秀率'
from Course c,
     Score s
where c.c_id = s.c_id
group by c.c_id, c_name;

它解:

select s.c_id,
       c.c_name,
       max(s.s_score),
       min(s.s_score),
       avg(s.s_score),
       sum(case when s.s_score >= 60 then 1 else 0 end) / count(s_id)                    '及格率',
       sum(case when s.s_score >= 70 and s.s_score < 80 then 1 else 0 end) / count(s_id) '中等率',
       sum(case when s.s_score >= 80 and s.s_score < 90 then 1 else 0 end) / count(s_id) '优良率',
       sum(case when s.s_score >= 90 then 1 else 0 end) / count(s_id)                    '优秀率'
from Score as s
         inner join Course as c on s.c_id = c.c_id
group by c_id

注意:这里使用sum的原因是,当满足条件为 1,则可以通过sum计数;count中,count(0)=1,只有count(null)=0,因此,使用count函数需要else null 才能不计数;


19.按各科成绩进行排序,并显示排名(重点 row_number)

窗口函数:

  • row_number() over(order by 列)

简单来说row_number()从1开始,为每一条分组记录返回一个数字。这里的row_number() over (order by slh desc)是把slh列降序,再为降序以后的每条slh记录返回一个序号。

row_number() over (partition by col1 order by col2)表示根据col1分组,在分组内部根据col2排序,而此函数计算的值就表示每组内部排序后的顺序编号,是连续且唯一的。

  • rank()
  • dense_rank()

排名row_number()

排名dense_rank()

排名rank()

1

1

1

2

2

2

3

2

2

4

3

4

没有重复值的排序

记录相同也是不重复的,可以进行分页使用;

连续排序

跳跃排序

符合成绩排法!

select *, rank() over (partition by c_id order by s_score desc) 'rank'
from Score

窗口函数最大的特点就是返回的结果集不发生变化,而且可以返回序号;group by只会返回统计量,不会返回分组后每条记录的值,这是其与group by最大的区别;

mysql数据库没有实现窗口函数,因此需要使用其他手段来解决这个问题;

完成对mysql数据库查询出的结果添加序号列”的需求。

1.row_number()函数效果实现

select s_id,c_id,s_score, (@curRank:=@curRank+1) as 'rank' from Score, (select @curRank:=0) as a order by s_score desc

2.dense_rank()函数效果实现

select s_id,c_id,s_score, if(@prevRank=s_score,@curRank,@curRank:=@curRank+1) as 'rank', @prevRank:=s_score from Score, (select @curRank:=0,@prevRank:=null) as a order by s_score desc

3.rank()函数效果实现

select b.s_id, b.c_id, b.s_score, b.rank from (select s_id, c_id, s_score, @curRank := if(@prevRank = s_score, @curRank, @incRank) as 'Rank', @incRank := @incRank + 1, @prevRank := s_score from Score, (select @curRank := 0, @prevRank := null, @incRank := 1) as a order by s_score desc) as b

 


20.查询学生的总成绩并进行排名

select s_id '学号', sum(s_score) '总成绩'
from Score
group by s_id
order by 总成绩 Desc
select s_id '学号', sum(s_score) '总成绩'
from Score
group by s_id
order by sum(s_score) Desc

注意:字段别名,as 和 引号都是别名的意思,不能同时使用;

区别名的时候加引号,order by的时候不需要加引号;


21.查询不同老师所教不同课程平均分从高到低显示;

自解:

select s.c_id, c.t_id, t.t_name, c.c_name, avg(s_score) 'avg_score'
from Score s
         inner join Course c on s.c_id = c.c_id
         left join Teacher t on c.t_id = t.t_id
group by s.c_id, c.t_id, c.c_name, t.t_name
order by avg_score desc

它解: 1.以课程为主体,求平均分 2.以老师为主体求平均分

select t.t_id, t.t_name, avg(sc.s_score) as avg_score
from Score as sc
         inner join Course as c on sc.c_id = c.c_id
         inner join Teacher as t on c.t_id = t.t_id
group by t.t_id, t.t_name
order by avg_score DESC


22.查询所有课程的成绩第2名到第3名的学生信息及该课程成绩

select *
from (select st.s_id,
             st.s_name,
             st.s_birth,
             st.s_sex,
             c_id,
             s_score,
             row_number() over (partition by c_id order by s_score desc) m
      from Score sc
               inner join Student st on sc.s_id = st.s_id) a
where m in (2, 3)

自解:

select s_id,
       c_id,
       s_score,
       @courRank := c_id,
       case
           when @courRank = c_id then @curRank := if(@prevRank = s_score, @curRank, @incRank)
           else (@curRank := 0 and @incRank := 1 and @courRank := c_id) end as 'rank',
       @incRank := @incRank + 1,
       @prevRank := s_score
from Score,
     (select @curRank := 0, @prevRank := null, @incRank := 1, @courRank := null) as r
order by c_id, s_score desc

 


23.使用分段【100-85)、【85-70)、【70-60)、【<60】来统计各科成绩,分别统计各分数段人数:课程ID和课程名称;(重点)

自解:

select s.c_id,
       c.c_name,
       sum(case when s_score >= 85 then 1 else 0 end)                  as '85-100分人数',
       sum(case when s_score >= 70 and s_score < 85 then 1 else 0 end) as '70-85分人数',
       sum(case when s_score >= 60 and s_score < 70 then 1 else 0 end) as '60-70分人数',
       sum(case when s_score < 60 then 1 else 0 end)                   as '60分以下人数'
from Score s
         inner join Course c on s.c_id = c.c_id
group by c.c_name, s.c_id

它解:

select c.c_id,
       c.c_name,
       sum(case when sc.s_score <= 100 and sc.s_score > 85 then 1 else 0 end) as '[100-85]',
       sum(case when sc.s_score <= 85 and sc.s_score > 70 then 1 else 0 end)  as '[85-70]',
       sum(case when sc.s_score <= 70 and sc.s_score > 60 then 1 else 0 end)  as '[70-60]',
       sum(case when sc.s_score <= 60 then 1 else 0 end)                      as '[<=60]'
from Score as sc
         inner join Course as c on sc.c_id = c.c_id gtoup by c.c_id,c.c_name

注意:sum与count进行统计的区别,count统计时,count(0)=1;count(null)=0


24.查询学生平均成绩及其名次

select s_id, avg(s_score), row_number() over (order by avg(s_score) desc)
from Score
order by s_id

#不想分组,over (order by avg(s_score)

 


26.查询每门课被选修的学生数

select c.c_id, c.c_name, count(distinct sc.s_id)
from Score as sc
         inner join Course as c on sc.c_id = c.c_id
group by c.c_id, c.c_name

 


27.查询出只有两门课程的全部学生的学号和姓名;

select sc.s_id, st.s_name
from Score sc
         inner join Student st on sc.s_id = st.s_id
group by sc.s_id
having count(distinct sc.c_id) = 2

子句查询:

select s_id.s_name
from Student
where s_id in (select s_id from Score group by s_id having count(distinct c_id) = 2)

in在数据量较大时效率更低,表连接在数据量较大时效率更高;


28.查询男生、女生人数

select sum(case when s_sex = '男' then 1 else 0 end) '男生人数', sum(case when s_sex = '女' then 1 else 0 end) '女生人数'
from Student
select s_sex ,count(s_id) from Student group by s_sex

 


29.查询名字中含有“风”字的学生信息

select *
from Student
where s_name like '%风%' -- 含有风 where s_name like '%风' 

-- 以风字结尾的姓名 where s_name like '风%' -- 以风字开头的姓名

like进行模糊查询,like %风%表示查询名字中带有风,like% 风表示


31.查询1990 年出生的学生名单(重点)

时间函数:

year

month

day
 

select *
from Student
where year(s_birth) = '1990'

时间格式的format转换;


32.查询平均成绩大于等于85分的所有学生的学号、姓名、平均成绩

select sc.s_id '学号', st.s_name '姓名', avg(sc.s_score) '平均成绩'
from Score sc
         inner join Student st on sc.s_id = st.s_id
group by sc.s_id
having 平均成绩 > 85

group by 用在where语句后面,先用where把常规条件筛选之后,再分组;

分组后,对分组结果进行筛选,使用having;


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

select s.c_id, c.c_name, avg(s.s_score) as avg_score
from Score as s
         inner join Course as c on s.c_id = c.c_id
group by s.c_id
order by avg_score asc, s.c_id desc

34.查询课程名称为‘数学’,且分数低于60的学生姓名和分数;

select st.s_name, c.c_name, sc.s_score
from Score as sc
         inner join Student st on sc.s_id = st.s_id
         inner join Course as c on c.c_id = sc.c_id
where c.c_name = '数学'
  and sc.s_score < 60

35、查询所有学生的课程及分数情况(重点)

自解:

select st.s_id,
       st.s_name,
       max(case when S.c_id = '01' then s_score else null end) '语文',
       max(case when S.c_id = '02' then s_score else null end) '数学',
       max(case when S.c_id = '03' then s_score else null end) '英语',
       max(case when S.c_id = '04' then s_score else null end) '化学'
from Student st
         left join Score S on st.s_id = S.s_id
         left join Course C on S.c_id = C.c_id
group by st.s_id, st.s_name

⚠️:查询所有学生的课程及分数情况,为了保证查出所有学生的信息,在进行连接的时候,首先使用学生表左连接课程成绩表,然后这张大表再去左链接课程表;因为第二次连接时,未选课同学的s.c_id=null 与课程表连接时这条记录会被删除,还是不能保证查询出所有学生的选课信息!

select * from Score as sc inner join Course as c on sc.c_id = c.c_id inner join Student as st on st.s_id = sc.s_id

注意:此时表中有sc.c_id、c.c_id、st.s_id、sc.s_id这些都是内链接产生的重复字段,没有去掉重复字段;

 

⚠️:group by 与case when组合使用的问题:

在group by之后,进行case when后,它会逐条进行判断,它每条判断完成之后,只会返回第一条记录;

例如:

case when c_name='数学'

组内记录逐条进行判断,但是返回的结果是第一条记录的返回值;

1

语文

90

null

 

数学

67

67

 

英语

46

null


 

36.查询课程成绩在70分以上课程名称,分数和学生姓名;

自解:

select st.s_name, c.c_name, sc.s_score
from Student st
         inner join Score sc on st.s_id = sc.s_id
         inner join Course c on sc.c_id = c.c_id
where sc.s_score > 70

37.查询不及格的课程并按课程号从大到小排列

自解:

select st.s_id, st.s_name, c.c_name, s.s_score, s.c_id
from Score s
         inner join Course c on s.c_id = c.c_id
         inner join Student st on st.s_id = s.s_id
where s_score < 60
order by s.c_id desc

38、查询课程编号为03且课程成绩在80分以上的学生的学号和姓名(不重要)

select s.s_id, s.s_name, sc.c_id, c.c_name, sc.s_score
from Student s
         inner join Score sc on s.s_id = sc.s_id
         inner join Course c on c.c_id = sc.c_id
where sc.c_id = '03'
  and sc.s_score > 80

39、求每门课程的学生人数(不重要)

select sc.c_id, c.c_name, count(distinct s_id)
from Score sc
         inner join Course c on sc.c_id = c.c_id
group by sc.c_id

40、查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top)

select st.s_id, st.s_name, sc.s_score, c.c_id, c.c_name
from Student as st
         inner join Score sc on st.s_id = sc.s_id
         inner join Course C on sc.c_id = C.c_id
         inner join Teacher t on c.t_id = t.t_id
where t.t_name = '张三'
order by sc.s_score desc
limit 0,1

note:通过order by desc+limit 实现取出第一名的同学

 

*limit的用法简介:

limit 0,1

第一个参数代表起始位置,索引从0开始,第二个参数表示取几条;

limit 1

表示取1条;


41.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)

解释:是找出一个所有课程成绩都相同的学生的学生信息;

select s_id from ( select s_id, s_score from Score group by s_id, s_score ) as a group by s_id having count(s_id) = 1

有问题:某位同学只修了一门课程,不能说明该同学课程成绩都相同;因此,需要选出的同学所修的课程数必须大于2;

自解:

select s_id
from (select s_id, s_score from Score group by s_id, s_score having count(s_id) > 1) as a
group by s_id
having count(s_id) = 1 

有个小问题:当Score中一个学生的同一门课程成绩不唯一时(存在补考成绩时),该解法不成立; 会将同一个学生,同一门课程,初始考试成绩与补考成绩相同的同学筛选出来;

 

它解:

select s_id
from (select b.s_id, b.s_score
      from Score as b
               inner join (select s_id from Score group by s_id having count(distinct c_id) > 1) as a on a.s_id = b.s_id
      group by s_id, s_score) as c
group by s_id
having count(s_id) = 1

思路:

1.查询出所修课程数目大于1的学生学号,然后将这些学号和Score表做inner join取交集,此时得到的这张表也就是所修课程大于1的学生;

2.对于这些学生,按照学号和成绩进行分组,作为c表

3.对c表的记录按照s_id学号进行分组,统计分组组内只有一条记录的同学,说明此同学所修课程成绩相同;


43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列(不重要)

自解:

select c_id '课程号', count(distinct s_id) '选修人数'
from Score
group by c_id
having count(distinct s_id) > 5
order by '选修人数' desc, '课程号' asc

它解:

 


44、检索至少选修两门课程的学生学号(不重要)

select s_id, count(distinct c_id)
from Score
group by s_id
having count(distinct c_id) >= 2

 


45、 查询选修了全部课程的学生信息(重点划红线地方)

自解:

select s_id, count(distinct c_id) '所修课程总数'
from Score
group by s_id
having count(distinct c_id) = (select count(distinct c_id) from Course)

它解:

select s_id, count(distinct c_id)
from Score
group by s_id
having count(distinct c_id) = (select count(distinct c_id) from Course)

46、查询各学生的年龄(精确到月份)

DATEDIFF()函数返回两个日期之间的天数;

语法:DATEDIFF(date1,date2)

date1和date2参数是合法的日期或日期/时间表达式

select s_id, s_birth, floor(Datediff(now(), s_birth) / 365) '年龄'
from Student

取整函数:

round()表示将值X四舍五入,无小数位;

round(X,D)表示将值X四舍五入为小数点后D位的数值,D为小数点后小数位数,若要保留X值小数点左边的D位,可将D设为负值;

floor():表示向下取整,只返回值X对整数部分,小数部分舍弃;


47、查询没学过“张三”老师讲授的任一门课程的学生姓名(还可以,自己写的,答案中没有)

自解:使用子查询

select s_id, s_name
from Student
where s_id not in (select distinct s.s_id
                   from Score s
                   where s.c_id in (select c_id
                                    from Teacher t
                                             inner join Course c on t.t_id = c.t_id
                                    where t.t_name = '张三'))

它解:使用inner join连接表 选出学过张三老师课程的学生,然后排除这些学生就可以了;

select *
from Student
where s_id not in (select distinct s_id
                   from Score as sc
                            inner join Course as c on sc.c_id = c.c_id
                            inner join Teacher t on c.t_id = t.t_id
                   where t.t_name = '张三')

48、查询两门以上不及格课程的同学的学号及其平均成绩

select s_id, avg(s_score)
from Score
group by s_id
having (sum(s_score < 60)) > 2

48.查询下周过生日的学生

select * from Student where week(s_birth, 1) = week(date(now()), 1) + 1

⚠️:同样的一天,在不同的年份里,代表的周数不一样。

解决办法:方法:换算为同一年,再进行比较

 

week函数:查看给定日期属于哪个周数,week(date,mode);

week接受两个参数:

  • date是要获取周数的日期;
  • mode是一个可选参数,用于确定周数计算的逻辑。它允许你指定本周是从星期一还是从星期日开始,返回的周数英在0-52之间或0-53之间;

#首先substring取出月日,然后concat拼接年份; -- select concat('2020-', substring('1990-01-20',6,5)) select * from Student where week(concat('2020-', substring(s_birth,6,5)), 1) = week(date(now()), 1) + 1

同一年当中,日期连续,具有可比性;

⚠️:依然存在年末问题,比如12-30是第53周,1月1日是第0周,但是在日历中,其实这两天是同一周;


49.查询本月过生日的人

select * from Student where month(s_birth)=month(now())

month:函数,可以直接取到月份;


50.查询下个月过生日的同学

select * from Student where month(s_birth)=month(now())+1

date(now()):只拿到当前时间的年月日,没有时分秒;

⚠️:没有考虑12月份+1等于13月份的情况;改进做法如下:

select * from Student where case when month(now())=12 then month(s_birth)=1 else month(s_birth)=month(now())+1 end

 

 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值