sql50题

https://zhuanlan.zhihu.com/p/43289968

建表:(成绩表中的score字段应该是int类型的,在建表语句中是varchar类型的了)

create table student(
    s_id varchar(10),
    s_name varchar(20),
    s_age date,
    s_sex varchar(10)
);

create table course(
    c_id varchar(10),
    c_name varchar(20),
    t_id varchar(10)
);


create table teacher (
t_id varchar(10),
t_name varchar(20)
);

create table score (
    s_id varchar(10),
    c_id varchar(10),
    score varchar(10)
);
建表Sql语句

向表中填充数据:

insert into student (s_id, s_name, s_age, s_sex)
values  ('01' , '赵雷' , '1990-01-01' , ''),
        ('02' , '钱电' , '1990-12-21' , ''),
        ('03' , '孙风' , '1990-05-20' , ''),
        ('04' , '李云' , '1990-08-06' , ''),
        ('05' , '周梅' , '1991-12-01' , ''),
        ('06' , '吴兰' , '1992-03-01' , ''),
        ('07' , '郑竹' , '1989-07-01' , ''),
        ('08' , '王菊' , '1990-01-20' , '');

insert into course (c_id, c_name, t_id)
values  ('01' , '语文' , '02'),
        ('02' , '数学' , '01'),
        ('03' , '英语' , '03');

insert into teacher (t_id, t_name)
values  ('01' , '张三'),
        ('02' , '李四'),
        ('03' , '王五');

insert into score (s_id, c_id, score)
values  ('01' , '01' , 80),
        ('01' , '02' , 90),
        ('01' , '03' , 99),
        ('02' , '01' , 70),
        ('02' , '02' , 60),
        ('02' , '03' , 80),
        ('03' , '01' , 80),
        ('03' , '02' , 80),
        ('03' , '03' , 80),
        ('04' , '01' , 50),
        ('04' , '02' , 30),
        ('04' , '03' , 20),
        ('05' , '01' , 76),
        ('05' , '02' , 87),
        ('06' , '01' , 31),
        ('06' , '03' , 34),
        ('07' , '02' , 89),
        ('07' , '03' , 98);
向表中填充数据的Sql语句

 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数。

select*from(
    select d.*,c.score from
        (select a.s_id,b.score chinese from 
            (select distinct s_id from score) a
        left join score b
            on a.s_id=b.s_id
        where b.c_id='01') d
    left join score c
        on c.s_id=d.s_id
        where c.c_id='02'    
    )e 
where e.chinese>e.score    
我的写法(只会使用left join)

 

select a.s_id,a.score,b.score from
    (select  s_id,c_id,score from score where c_id='01')a
inner join
    (select s_id,c_id,score from score where c_id='02')b
on a.s_id=b.s_id
where a.score>b.score
好的写法

使用inner join的性能要比left join的性能要好很多!

2、查询平均成绩大于等于60分的同学的学生编号和平均成绩。

select * from(
    select s_id,avg(score) avgScore from score group by s_id)a 
where a.avgScore>60
我的写法(不会使用having条件语句)

 

select s_id,avg(score) from score group by s_id having avg(score)>60
好的写法

使用having条件语句,一条sql就可以搞定了!不需要写子查询了。

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

select c.*,d.num from
     (    
        select a.s_id,a.s_name,b.score from
            (select s_id,s_name from vichin_student) a
                inner join
            (select s_id,SUM(score) score from vichin_score group by s_id) b
        on a.s_id=b.s_id
     ) c
     inner join
     (select s_id,count(s_id) num from vichin_score group by s_id) d
     on c.s_id=d.s_id
我的写法

 

select a.s_id,a.s_name,count(b.c_id) num, sum(case when b.score is null then 0 else b.score end) score
        from vichin_student as a
    left join
        vichin_score as b 
    on a.s_id=b.s_id
group by a.s_id,a.s_name
好的写法

审题出现偏差,题目中要求选出所有学生的信息,而我写的是:将所有有成绩的学员信息给选出来。因为题目是查询出所有学生,所以此处应该使用left join,并以学生表student为主表进行关联查询。

4、查询姓“侯”的老师的个数。

select  COUNT(t_id) from vichin_teacher where  t_name like '侯%'

select  COUNT(distinct t_id) from vichin_teacher where  t_name like '侯%'

5、查询没有学过张三老师课的学生的学号。

 这道题写出来的sql语句是错的。

Sql语句中的子查询,可以使用inner join来替换。

select distinct s_id,s_name from vichin_student  where s_id not in
(
    select s_id from vichin_score 
        where c_id =(
            select c_id from
                (select t_id from vichin_teacher where t_name='张三') a
                inner join
                vichin_course b
                on a.t_id=b.t_id
        )
)
好的写法

思路:先写查询出所有参加过张三老师课程的学生,最后在not in 一下就能得出没参加过张三老师课程的学生了。

6、查询出学过张三老师所教授课程的所有学生的学号和姓名。

    select e.s_id,e.s_name from
        (select t_id from Vichin_teacher where t_name='张三')a
        inner join
        Vichin_course b
        on a.t_id=b.t_id
        inner join
        Vichin_score d
        on d.c_id=b.c_id
        inner join Vichin_student e
        on d.s_id=e.s_id
我的写法

思路与视频中的一致。

7、查询学过编号为01的课程,并且也学过编号为02的课程的学生的学号和姓名。

select  a.s_id,b.s_name from
    (select distinct s_id from Vichin_score where c_id='02' or c_id='01') a
    inner join
    Vichin_student b
    on a.s_id=b.s_id
我的写法

写法有问题,审题有误。我的写法求出的结果是学过01或者是学过02课程的学生。

    select c.s_id,d.s_name from(
         select a.s_id from 
         (select distinct s_id,c_id from Vichin_score where c_id='02')a
          inner join 
          (select distinct s_id,c_id from Vichin_score where c_id='01')b
          on a.s_id=b.s_id
    ) c 
        inner join
    Vichin_student d
    on c.s_id=d.s_id
好的写法

思路:先使用inner join查询出学过01、02课程的学生之后,既可以使用子查询,也可以使用连接查询。因为连接查询的性能优于子查询,所以使用了连接查询。

 8、查询课程编号为02的总成绩。

select c_id,sum(score) totalScore,AVG(score) avgScore,COUNT(s_id) totalStudent from vichin_score where c_id='02'

select c_id,sum(score) totalScore,AVG(score) avgScore,COUNT(s_id) totalStudent from vichin_score group by c_id

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

 没有写出Sql语句。

select c.s_id,d.s_name from
    (
    select distinct a.s_id from    
        (select s_id,COUNT(c_id) cls from Vichin_score 
            where score<60
            group by s_id)a
            inner join 
            (select s_id,COUNT(c_id) cls from Vichin_score
            group by s_id)b
            on a.cls=b.cls and a.s_id=b.s_id
    )c
    inner join
    Vichin_student d
    on c.s_id=d.s_id
好的写法

思路:

  1、先得出成绩小于60分的同学

select s_id,COUNT(c_id) cls from Vichin_score 
where score<60
group by s_id
先得出成绩小于60的分的同学

  2、统计该同学总共学了几门课

select s_id,COUNT(c_id) cls from Vichin_score
group by s_id
统计该同学总共学了几门课

  3、如果第一条sql语句取出的cls(课程数)数量等于第二条sql语句取出的cls(课程数),就说明这个同学所有的课程都不及格。

select * from
(select s_id,COUNT(c_id) cls from Vichin_score 
            where score<60
            group by s_id)a
inner join
(select s_id,COUNT(c_id) cls from Vichin_score
            group by s_id)b
on a.cls=b.cls and a.s_id=b.s_id
找出符合条件的同学的学号。

 10、查询没有学全所有课程的学生的学号、姓名。

    select * from(
        select s_id,COUNT(c_id) cls from Vichin_score group by s_id
        )a where a.cls<3
我的写法

 

--1、先使用左连接查询,得出所有学生的考试信息
--2、再获取课程数量,并且使用统计函数count,统计所有学生的报考科目的数量,再比较学生报考科目数量与课程数量即可。
select a.s_id,a.s_name from
    (
    select st.s_id,st.s_name,sc.c_id from Vichin_student as st
    left join Vichin_score sc 
    on st.s_id=sc.s_id
    ) a  group by a.s_id,a.s_name having COUNT(c_id)<(select COUNT(distinct(c_id))from Vichin_course)
好的写法

思路:不应该固定写死3那个数字,因为当后面课程数量发生变化的时候,sql语句就无效了。还有、王菊同学一门课都没有考,他的名字就完全不会出现在score表,在统计的时候就会漏掉。

11、查询至少有一门课与学号“01”的学生所学课程相同的学生的学号和姓名。

select distinct s_id from Vichin_score where c_id  in(
select c_id from Vichin_score where s_id='01') and s_id !='01'
我的写法

12、查询和“01”号同学所学课程完全相同的其他同学的学号。

--先找出01所选修的课程01,02,03,再使用in找出选修了这3门课程的同学。其中07同学之选修了2门,在选修课程的数量上是有问题的。
select s_id from Vichin_score where c_id in(
    select c_id from Vichin_score where s_id='01'
) and s_id!='01' group by s_id having COUNT(c_id)=(select COUNT(distinct c_id) from Vichin_score where s_id='01')
我的写法

 

select s_id from Vichin_student where s_id in(
    select s_id from Vichin_score where s_id!='01' group by s_id having COUNT(distinct c_id)=(select COUNT(distinct c_id) from Vichin_score where s_id='01')
)  
and s_id not in(
    select distinct s_id from Vichin_score where c_id not in (select c_id from Vichin_score where s_id='01')
)
视频中的写法

感觉我的写法不比视频中的写法差。

15、查询两门及以上不合格课程的同学的学号、姓名及其平均成绩。

select a.s_id,c.s_name,AVG(score) score from 
        (select s_id from Vichin_score where score<60 group by s_id having COUNT(c_id)>=2)a
        inner join
        Vichin_score b
        on a.s_id=b.s_id
        inner join
        Vichin_student c
        on c.s_id=a.s_id
        group by a.s_id,c.s_name
我的写法

视频中的写法与我的写法大体相同。

16、检索“01”课程分数小于60,按分数降序排列的学生信息。

select b.s_id,score from Vichin_score a 
inner join
Vichin_student b
on a.s_id=b.s_id
where c_id='01' and score<60 
order by a.score desc
我的写法

17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩。

 审题有误,所以就不展示我的写法。

select s_id,
MAX(case when c_id='01' then score else null end) '语文',
MAX(case when c_id='02' then score else null end) '数学',
MAX(case when c_id='03' then score else null end) '英语',
AVG(score) '平均成绩'
from Vichin_score
group by s_id
order by AVG(score) desc
好的写法

思路:

   因为group by后面的字段需要与select后面的字段一样,所以将MAX函数去掉,group by就需要加上case when这段语句。很明显在group by 后面加上case语句去分组,是不符合逻辑的(不可能根据score去分组)。但是如果select的字段加上了聚合函数,那么就不需要出现在group by后面了。因为每个学生 “01”的课程只有1们,所以使用MAX函数也不会错。所以这里就使用MAX函数来解决问题。null值代表没有考过。

18、查询各科最高分、最低分和平均分:以如下形式显示:课程ID,课程名称、最高分、最低分、平均分,及格率,中等率,优良率,优秀率,及格为<=60,中等为70-80,优良为80-90,优秀为<=90。

select a.c_id,b.c_name,max(score) maxS ,min(score) minS,AVG(score) avgS, 
CONVERT(decimal,SUM(case when score>60 then 1 else 0 end))/COUNT(score) '及格率',
CONVERT(decimal,SUM(case when score>=70 and score<80 then 1 else 0 end))/COUNT(score) '中等率',
CONVERT(decimal,SUM(case when score>=80 and score<90 then 1 else 0 end))/COUNT(score) '优良率',
CONVERT(decimal,SUM(case when score>=90 then 1 else 0 end))/COUNT(score) '优秀率'
from Vichin_score a
inner join
Vichin_course b
on a.c_id=b.c_id
group by a.c_id,b.c_name
好的写法

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

select ROW_NUMBER() over(order by num desc) num,a.s_id,a.c_id,a.score from(
select s_id,c_id,score,num=1 from Vichin_score
group by c_id,s_id,score)a
order by c_id,score desc
我的写法

视频中没有讲解(my Sql中没有ROW_NUMBER 函数)。

20、查询学生的总成绩,并进行排名。

select ROW_NUMBER()over(order by a.totalScore desc) num,a.s_id,a.totalScore from(
select s_id,sum(score) totalScore from Vichin_score 
group by s_id)a
order by a.totalScore desc
我的写法

与视频中的写法一致。(视频中没有使用ROW_NUMBER函数进行排名)。

21、查询不同老师所教不同课程的平均分,从高到低排序。

select a.c_id,b.c_name,AVG(score) avgScore from Vichin_score a
inner join
vichin_course b
on a.c_id=b.c_id
group by a.c_id,b.c_name
order by avgScore
我的写法

与视频中的思路差不多。

22、查询所课程的成绩 第2名到第3名的学生信息以及课程成绩。

select * from
(
    select a.s_id,a.s_name,a.s_age,a.s_sex,b.c_id,score,ROW_NUMBER() over(partition by b.c_id order by b.score desc) num from Vichin_score b
    inner join
    Vichin_student a
    on a.s_id=b.s_id
)c
where c.num in(2,3)
视频中的写法

我的思路是错的,所以就没贴出来。

思路:使用ROW_NUMBER() over(partition by b.c_id order by b.score desc) 给分数进行分组排序,分组字段是partition后面跟着的c_id。然后再使用where条件语句 in(2,3)就能得出结果了。

很久没有用到partition进行分组了,所以忘记了这个知识点。

23、使用分段(100-85)、(85-70)、(70-60),(<60) 来统计各科成绩,分别统计各分数段人数,课程ID和课程名称。

select b.c_id,b.c_name,
SUM(case when score<=100 and score>85 then 1 else 0 end) '85以上',
SUM(case when score<=85 and score>70 then 1 else 0 end) '85~70',
SUM(case when score<=70 and score>=60 then 1 else 0 end) '70~60',
SUM(case when score<60 then 1 else 0 end) '60以下'
from Vichin_score a
inner join
Vichin_course b
on a.c_id=b.c_id
group by b.c_id,b.c_name
视频中的写法

感觉跟前面的一题思路相似,都是使用case when。

24、查询学生的平均成绩及其名次。

select s_id,AVG(score) avgScore,ROW_NUMBER() over(order by AVG(score) desc) num from Vichin_score
group by s_id
我的写法

我的写法与视频中的写法一致。

25、查询各科成绩前三名的记录(不考虑成绩并列情况)(重点 与22题类似)

select a.c_id,
Max(case when a.num=1 then a.score else null end) as '第一名',
Max(case when a.num=2 then a.score else null end) as '第二名',
Max(case when a.num=3 then a.score else null end) as '第三名'
 from
    (
    --下面这条子句就已经能够查询出结果了,但是横置过来的数据,增加上面那部分语句是将整个结果进行了转置
    select c_id,score,num,s_name from(
    select c_id,s_id,score,ROW_NUMBER() over(partition by c_id order by score) as num from Vichin_score)s
    inner join --这里可以用子查询,去将num为1,2,3的给过滤出来,但是使用关联查询,性能更好。where num in(1,2,3) 
    Vichin_student st
    on st.s_id=s.s_id    
    where s.num in(1,2,3)
)a
group by c_id
知乎上的写法

26、查询每门课程被选修的学生数。

select COUNT(s_id) '学生数量',c_id from Vichin_score group by c_id
我的写法

视频中的写法是将课程的名称也显示出来,只要去inner join course表就能出来了。

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

select COUNT(s.c_id) num ,s.s_id,st.s_name from Vichin_score s 
inner join 
vichin_student st
on st.s_id=s.s_id
group by s.s_id,st.s_name
having COUNT(s.c_id)=2
我的写法

 28、查询男生、女生人数。

我的想法是使用 Sum(case when) 来实现,感觉没有视频中的写法好。

select COUNT(s_id),s_sex from vichin_student group by s_sex
视频中的写法

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

select *from vichin_student where s_name like '%风%'
我的写法

31、查询1990年出生的学生名单。

select * from vichin_student where s_age like '1990%'
我的写法
--视频中写法
select * from Vichin_student where YEAR(s_age)=1990

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

select st.s_id,st.s_name,AVG(score) avgScore from Vichin_score s  
inner join
Vichin_student st
on s.s_id=st.s_id
group by st.s_id,st.s_name 
having AVG(score)>85
我的写法

写法一致

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

select c_id,AVG(score) from Vichin_score 
group by c_id
order by AVG(score),c_id desc
我的写法

写法一致

34、查询课程名称为"数学",且分数低于60的学生姓名和分数。

select s.s_id,st.s_name,score from Vichin_course c
inner join
Vichin_score s
on c.c_id=s.c_id
inner join
Vichin_student st
on st.s_id=s.s_id
where c_name='数学' and s.score<60
视频中的写法

思路:这道题目有两种实现方法。

  我的想法是先对course 和 student 表使用inner join筛选一下,然后对筛选的结果使用子查询。

  视频中的写法是对3个表直接使用inner join,这里因为数据量不是很大,所以直接对3个表使用inner join 是可以的。如果表中的数据量很大的时候,使用临时表或者子查询效果会好一些,特别是临时表。

35、查询所有学生的课程及分数情况。

下面这是我写的,视频中的sql语句还将学生的姓名也查询了出来

select s_id, 
max(case when c_id='01' then score else null end)'语文',
max(case when c_id='02' then score else null end)'数学',
max(case when c_id='03' then score else null end)'英语'
from Vichin_score
group by s_id
1 01 90
02 67
03 46

如果不使用max函数,不使用group by,那么得到的结果是上面这样的一个表格。

当对课程编号01这一列进行判断的时候,第1行01符合条件,那么结果就是90,当进行到第2行的时候,发现值是02不是,那么结果会是null,第3行也是null。这些null值会呈现在查询结果中,是不符合预期的。

加了max之后,在查询结果中只会去取90这个数据,另外的2个null值是不会被选取的。

同理 当对编号02进行判断的时候,01和03是不会选取的。最终的结果是正确的,即:在判断01的时候,我只会取01所对应的值,02只会取02的值,03只会取03的值。

36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数。

select st.s_name,s.score,c.c_name from Vichin_score s
inner join
Vichin_student st
on s.s_id=st.s_id
inner join
Vichin_course c
on c.c_id=s.c_id
where score>70
我的写法

与视频中一致

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

select * from Vichin_score where score<60 order by c_id
我的写法

与视频一致

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

select st.s_id,st.s_name,score from Vichin_score s
inner join
Vichin_student st
on s.s_id=st.s_id
where c_id='03' and score>=80
我的写法

与视频一致

39、求每门课程的学生人数。

select c_id,COUNT(s_id)'人数' from Vichin_score
group by c_id
我的写法

与视频一致

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

select top 1 st.s_name,s.s_id,score from Vichin_teacher t
inner join
Vichin_course c
on t.t_id=c.t_id
inner join 
Vichin_score s
on s.c_id=c.c_id
inner join
Vichin_student st
on st.s_id=s.s_id
where t_name='张三'
order by s.score desc
我的写法

与视频一致

41、查询不同课程(语、数、英)成绩相同的学生的学生编号、课程编号、学生成绩。

select s.s_id,s.c_id,s.score from
    ( select  s_id,score from Vichin_score
      group by s_id,score
      having count(c_id)=3
    )a
inner join
Vichin_score s
on a.s_id=s.s_id
我的写法

不适用聚合函数,也能查询到相应结果。

select distinct a.s_id,a.c_id,a.score
from Vichin_score a
inner join
Vichin_score b
on a.s_id=b.s_id
where a.score=b.score and a.c_id!=b.c_id
教程中的写法

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

select c_id,COUNT(s_id)'人数' from Vichin_score
group by c_id
having COUNT(s_id)>5
order by COUNT(s_id),c_id
我的写法

与视频一致

44、检索至少选修两门课程的学生学号。

select s_id,count(c_id)'课程数' from Vichin_score
group by s_id
having count(c_id)>=2
我的写法

45、查询选修了全部课程的学生信息。

select s_id,COUNT(c_id) from Vichin_score
group by s_id
having COUNT(c_id)=(select COUNT(c_id) from Vichin_course)
我的写法

46、查询各学生的年龄。

select s_name,DATEDIFF(YY,s_age,GETDATE()) '年龄' from Vichin_student
我的写法

47、查询本月过生日的学生。

select s_name from Vichin_student
where month(s_age)=datepart(month,getdate())
我的写法

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

select s_id,AVG(score)'平均成绩' from Vichin_score
where score<60
group by s_id
having COUNT(c_id)>=2
我的写法

49、查询下月过生日的同学。

select s_name from Vichin_student
where month(s_age)=datepart(month,getdate())+1
我的写法

50、查询本周过生日的同学。

 不会...Sql中没有week函数,mySql中有这个函数。

posted @ 2020-03-22 15:47  水墨晨诗  阅读(428)  评论(0编辑  收藏  举报