sql server 常见面试题

来自  《对比Excel,轻松学习SQL数据分析》

每个都有敲过现在记录

1 查询每个班学生数

现在有一张全校学生信息表stu_table,这张表存储了每位学生的id、name(姓名)、class(班级)、sex(性别)以及一些其他信息,现在我们想知道每个班有多少学生,该怎么实现呢?

stu_table表如下所示:

id name class sex
4 张文华 二班
3 李思雨 一班
1 王小凤 一班
7 李智瑞 三班
6 徐文杰 二班
8 徐雨秋 三班
5 张青云 二班
9 孙皓然 三班
10 李春山 三班
2 刘诗迪 一班

自己先想一下代码怎么写,然后再参考我的代码。

1 select 
2     class
3     ,count(id) as stu_num
4 from
5     demo.stu_table
6 group by
7     class

 

 

解题思路:

我们是要获取每个班的学生数,首先需要对班级进行分组,使用的是group by;然后再对每个组内的学生进行计数聚合运算,使用的count。最后运行结果如下:

class stu_num
二班 3
一班 3
三班 4

2 查询每个班男女学生数

还是前面的全校学生信息表stu_table,现在我们想知道每个班男生女生分别有多少个?

自己先想一下代码怎么写,然后再参考我的代码。

select 
    class
    ,sex
    ,count(id) as stu_num
from
    demo.stu_table
group by
    class
    ,sex

 

 

解题思路:

与第一题不同的是,不仅需要每个班级的信息,还需要每个班级里面男女生分别的信息,主要考察的就是按照多列分组聚合的知识,直接在group by后面指明要分组的多列即可,且列与列之间用逗号分隔开。最后运行结果如下:

class sex stu_num
二班 2
一班 3
三班 4
二班 1

3 姓张的同学有多少个

还是前面的全校学生信息表stu_table,现在我们想知道这张表中姓张的同学有多少个?

自己先想一下代码怎么写,然后再参考我的代码。

select 
    count(id) as stu_num
from
    demo.stu_table
where name like "张%"

 

 

解题思路:

我们是要获取姓张的同学有多少个,首先需要思考的是怎么去判断同学是否姓张,假设我们表里面存储的姓名都是先姓后名的形式,那就可以用到字符串匹配函数like;知道怎么判断同学是否姓张,接下来就是把这些同学筛选出来,使用的是where条件;最后针对筛选出来的同学进行计数,使用的是count。最后运行结果如下:

stu_num
2

4 筛选出id第3-5的同学

还是前面的全校学生信息表stu_table,现在我们要获取id从小到大排序以后第3-5位的同学的信息。

自己先想一下代码怎么写,然后再参考我的代码。

select 
    *
from
    demo.stu_table
order by id asc
limit 2,3

 

 

解题思路:

我们要获取id从小到大排序以后第3-5位的同学,因为不确定id是否连续,所以我们没法直接用where条件来筛选id。我们先对id进行升序排列,然后再利用limit进行筛选。最后运行结果如下:

id name class sex
3 李思雨 一班
4 张文华 二班
5 张青云 二班

5 筛选出挂科的同学

现在有一张学生成绩表score_table,这张表存储了每位学生的id、name(姓名)、class(班级)、score(成绩),现在我们想要把挂科(成绩小于60)的同学信息筛选出来。

score_table表如下所示:

id name class score
1 王小凤 一班 88
2 刘诗迪 一班 70
3 李思雨 一班 92
4 张文华 二班 55
5 张青云 二班 77
6 徐文杰 二班 77
7 李智瑞 三班 56
8 徐雨秋 三班 91
9 孙皓然 三班 93
10 李春山 三班 57

自己先想一下代码怎么写,然后再参考我的代码。

select 
    *
from
    demo.score_table
where score < 60

 

 

解题思路:

我们要获取挂科同学的信息,只需要加一个where条件用来限定挂科这个条件即可。最后运行结果如下:

id name class score
4 张文华 二班 55
7 李智瑞 三班 56
10 李春山 三班 57

6 筛选姓张的且挂科的同学

我们现在需要根据学生成绩表score_table查找出姓张的且挂科的同学的信息。

自己先想一下代码怎么写,然后再参考我的代码。

select 
    *
from
    demo.score_table
where score < 60
    and name like "张%"

 

 

解题思路:

这里面主要是用到了多条件筛选,多个条件之间用and进行关联即可。最后运行结果如下:

id name class score
4 张文华 二班 55

7 查询销冠获得次数

我们有一张表month_table记录了每月的销售冠军信息,这张表存储了每月销冠的id、name(姓名)、month_num(月份),现在需要获取销冠次数超过2次的人以及其对应的做销冠次数。

month_table表如下所示:

id name month_num
E002 王小凤 1
E001 张文华 2
E003 孙皓然 3
E001 张文华 4
E002 王小凤 5
E001 张文华 6
E004 李智瑞 7
E002 王小凤 8
E003 孙皓然 9

自己先想一下代码怎么写,然后再参考我的代码。

select 
    id
    ,name
    ,count(month_num) num
from 
    demo.month_table
group by 
    id
    ,name
having 
    count(month_num) > 2

 

 

解题思路:

我们要获取销冠次数超过2次的人以及其对应的做销冠次数,首先需要获取每个人做销冠的次数,对id进行group by,然后在组内对month_num进行计数即可;然后再对分组聚合后的结果利用having进行条件筛选。最后结果如下:

id name num
E002 王小凤 3
E001 张文华 3

8 获取每个部门一整年业绩提升幅度

现在有一个月份销售额记录表sale_table,这个表记录了每年每月的销售额,现在我们想看下今年(2019年),月销售额最高涨幅是多少?

sale_table表如下所示:

year_num month_num sales
2019 1 2854
2019 2 4772
2019 3 3542
2019 4 1336
2019 5 3544
2018 1 2293
2018 2 2559
2018 3 2597
2018 4 2363

自己先想一下代码怎么写,然后再参考我的代码。

select 
    max(sales) as max_sales
 ,min(sales) as min_sales
    ,max(sales)-min(sales) as cha
    ,(max(sales)-min(sales))/min(sales) as growth
from 
    demo.sale_table
where 
    year_num = 2019

 

 

解题思路:

我们要获取今年的最大月涨幅,首先需要通过where条件把今年的每月数据销售额筛选出来;然后再在今年的月销售额里面寻找最大和最小的销售额,对两者进行做差,就是我们想要的结果。最后运行结果如下:

max_sales min_sales cha growth
4772 1336 3436 2.5719

9 查找每科成绩大于70的学生

我们有一张学生科目成绩表score_info_table,这张表记录了每一位同学每一科目的成绩,每一位同学的每科成绩是一行,现在我们想要通过这张表获取到每科成绩都大于70分的学生。

score_info_table表如下所示:

id name subject score
1 王小凤 语文 88
2 张文华 数学 70
3 徐雨秋 英语 92
1 王小凤 语文 55
2 张文华 数学 77
3 徐雨秋 英语 77
1 王小凤 语文 72
2 张文华 数学 91
3 徐雨秋 英语 93

自己先想一下代码怎么写,然后再参考我的代码。

select 
 id
    ,name
 from
    demo.score_info_table
 group by
    id
    ,name
 having 
    min(score) > 70  

 

  

解题思路:

我们是要获取每科成绩大于70的学生,只要能够保证最小成绩是大于70分的,就说明这位同学每科成绩都大于70分.所以第一步就是先获取每位同学的最小成绩,先对name进行group by分组,再在组内求最小值,然后将最小成绩大于70分的同学通过having筛选出来即可。最后运行结果如下:

id name
3 徐雨秋

10 删除重复值

现在有一个学生信息表stu_info_table,这张表存储了每位学生id、name(姓名)、class(班级)、grade(年级),现在我们想获取这个学校所有年级以及所有班级的信息,即哪些年级有哪些班级,该怎么获取?

stu_info_table表如下所示:

id name class grade
1 王小凤 一班 一年级
2 刘诗迪 一班 二年级
3 李思雨 一班 一年级
4 张文华 二班 二年级
5 张青云 二班 一年级
6 徐文杰 二班 二年级
7 李智瑞 一班 一年级
8 徐雨秋 二班 二年级
9 孙皓然 一班 一年级

自己先想一下代码怎么写,然后再参考我的代码。

select
    grade
    ,class
from
    demo.stu_info_table
group by 
    grade
    ,class
order by
    grade

 

 

解题思路:

stu_table表中id列是主键,即不重复的,但是class和grade是重复的,多个id会属于同一个class和grade。我们只要class和grade信息,所以是需要对这两列进行去重,去重我们除了用distinct以外,还可以用group by。最后运行结果如下:

grade class
一年级 一班
一年级 二班
二年级 一班
二年级 二班

 

11 行列互换

现在我们有下面这么一个表row_col_table,这个表中每年每月的销量是一行数据:

year_num month_num sales
2019 1 100
2019 2 200
2019 3 300
2019 4 400
2020 1 200
2020 2 400
2020 3 600
2020 4 800

我们需要把上面这种纵向存储数据的方式改成下表所示的横向存储:

year_num m1 m2 m3 m4
2019 100 200 300 400
2020 200 400 600 800

自己先想一下代码怎么写,然后再参考我的代码。

select
    year_num
    ,sum(case when month_num = 1 then sales end) as m1
    ,sum(case when month_num = 2 then sales end) as m2
    ,sum(case when month_num = 3 then sales end) as m3
    ,sum(case when month_num = 4 then sales end) as m4
from
    demo.row_col_table
group by
    year_num

 

 

解题思路:

我们要把纵向数据表转换成横向数据表,首先是把多行的年数据转化为一年是一行,可以通过group by实现;group by一般需要与聚合函数一起使用,但是不是对所有数据进行聚合,所以我们通过case when来达到对指定月份数据进行聚合。

12 多列比较

现在表col_table中有col_1、col_2、col_3三列数据,我们需要根据这三列数据生成最后一列结果列,结果列的生成规则为:如果col_1大于col_2时选col_1列,如果col_2大于col_3列时选col_3列,否则选col_2列。

col_table表如下所示:

col_1 col_2 col_3
5 10 7
1 10 6
9 3 5
5 2 9
10 4 3
5 2 9
5 8 6
8 8 6

自己先想一下代码怎么写,然后再参考我的代码。

select
 col_1
 ,col_2
 ,col_3
 ,(case when col_1 > col_2 then col_1
     when col_2 > col_3 then col_3
   else col_2
   end) as all_result
from
 demo.col_table

 

 

解题思路:

这个多列比较其实就是一个多重判断的过程,借助case when即可实现,先去判断col_1和col_2的关系,然后再去判断col_2和col_3的关系。这里需要注意一下各判断的执行顺序,先去执行第一行case when,然后再去执行第二行的。最后运行结果如下:

col_1 col_2 col_3 all_result
5 10 7 7
1 10 6 6
9 3 5 9
5 2 9 5
10 4 3 10
5 2 9 5
5 8 6 6
8 8 6 6

13 对成绩进行分组

现在有一个某科目的学生成绩表subject_table,这张表存储了每位学生的id、score(成绩)以及其他信息,我们想知道60分以下、60-80分、80-100分这三个成绩段内分别有多少学生,该怎实现呢?

subject_table表如下所示:

id score
1 56
2 91
3 67
4 54
5 56
6 69
7 61
8 83
9 99

自己先想一下代码怎么写,然后再参考我的代码。

select
    (case 
        when score < 60 then "60分以下"
        when score < 80 then "60-80分"
        when score < 100 then "80-100分"
    else "其他"
    end) as score_bin
    ,count(id) as stu_cnt
from
    demo.subject_table
group by 
    (case 
        when score < 60 then "60分以下"
        when score < 80 then "60-80分"
        when score < 100 then "80-100分"
    else "其他"
    end)

 

 

解题思路:

我们现在需要知道每个成绩段内的学生数,需要做的第一件事就是对成绩进行分段,利用的就是case when,对成绩分段完成以后再对分段结果进行group by,然后再在组内计数获得每个分段内的学生数。最后运行结果如下:

score_bin stu_cnt
60分以下 3
80-100分 3
60-80分 3

14 周累计数据获取

现在我们有一个订单明细表order_table,这张表中存储了order_id(订单id)、order_date(订单日期)以及其他订单相关信息,现在我们需要每天获取本周累计的订单数,本周累计是指本周一到获取数据当天,比如今天是周三,那么本周累计就是周一到周三。这个该怎么实现呢?

order_table表如下所示:

order_id order_date
1 2019/1/8
2 2019/1/9
3 2019/1/10
4 2019/1/11
5 2020/1/8
6 2020/1/9
7 2020/1/10
8 2020/1/11
9 2020/1/12

自己先想一下代码怎么写,然后再参考我的代码。

select
    curdate()
    ,count(order_id) as order_cnt
from
    demo.order_table
where 
    weekofyear(order_date) = weekofyear(curdate())
    and year(order_date) = year(curdate())

 

 

解题思路:

我们是要获取本周累计的订单数,只需要把本周的订单明细筛选出来,然后对订单id进行计数就是我们想要的。那该怎么把本周的订单明细筛选出来呢?让订单日期所属的周与程序运行当日所属的周是一个周,且所属的年是同一年。后面这个条件一定要注意,因为周数在不同年份是会重复的,但是在同一年内是不重复的。比如2019年有一个52周,2020年也会有,但是不会在一年里面出现两个52周。最后运行结果如下:

cur_date order_cnt
2020-01-12 5

15 周环比数据获取

我们现在需要根据订单明细表order_table,获取当日的订单数;当日的环比订单数,即昨天的数据。

自己先想一下代码怎么写,然后再参考我的代码。

select
    count(order_id) as order_cnt
    ,count(if(date_sub(curdate(),interval 1 day) = order_date,order_id,null)) last_order_cnt
from
    demo.order_table

 

 

解题思路:

当日的订单数比较好获取,主要是环比数据的获取,当订单日期等于当日日期向前偏移1天的日期时,对order_id进行计数就是昨日的订单数。这里面需要注意的是,当if条件不满足时,结果为null,而不能是别的,因为count(null)=0,而count()其他内容不等于0。最后运行结果如下:

order_cnt last_order_cnt
9 1

16 查找获奖同学信息

现在有一张学生信息表table1,这张表记录了id、name等一些其他信息;还有另外一张获奖名单表table2,这张表记录了获奖学生的id和name。现在我们想要通过table1获取获奖学生的更多信息。

table1表如下所示:

id name
1 王小凤
2 刘诗迪
3 李思雨
4 张文华
5 张青云
6 徐文杰
7 李智瑞
8 徐雨秋
9 孙皓然

table2表如下所示:

id name
1 王小凤
2 刘诗迪
3 李思雨
7 李智瑞
8 徐雨秋
9 孙皓然

自己先想一下代码怎么写,然后再参考我的代码。

select
    table1.*
from
    demo.table1
left join
    demo.table2
    on table1.id = table2.id
where
    table2.id is not null

 

 

解题思路:

我们要获取获奖同学的全部信息,已知table1表中存储了全部学生的全部信息,我们用table1去左连接table2,如果该同学有获奖,就会在table2中能找到,反之则找不到。所以我们就可以利用table2的id是否为空来判断该同学有没有获奖,进而把我们想要的信息通过where条件筛选出来。最后运行结果如下:

id name
1 王小凤
2 刘诗迪
3 李思雨
7 李智瑞
8 徐雨秋
9 孙皓然

17 计算用户留存情况

现在有一张用户登陆表user_login,这张表记录了每个用户每次的登陆时间,uid(用户id)和login_time(登陆时间)。我们想看用户的次日留存数、三日留存数、七日留存数,只要用户从首次登陆以后再有登陆就算留存下来了,该怎么实现呢?

user_login表如下所示:

uid login_time
1 2019/1/1 6:00
1 2019/1/2 10:00
1 2019/1/4 19:00
2 2019/1/2 10:00
2 2019/1/3 9:00
2 2019/1/9 14:00
3 2019/1/3 8:00
3 2019/1/4 10:00

自己先想一下代码怎么写,然后再参考我的代码。

select
    (case when t3.day_value = 1 then "次日留存"
          when t3.day_value = 3 then "三日留存"
          when t3.day_value = 7 then "七日留存"
    else "其他"
    end) as type
    ,count(t3.uid) uid_cnt
from
    (select
        t1.uid
        ,t1.first_time
        ,t2.last_time
        ,datediff(t2.last_time,t1.first_time) day_value
    from
        (select
            uid
            ,date(min(login_time)) as first_time
        from
            demo.user_login
        group by
            uid)t1
    left join
        (select
            uid
            ,date(max(login_time)) as last_time
        from
            demo.user_login
        group by
            uid)t2
    on t1.uid = t2.uid)t3
group by
        (case when t3.day_value = 1 then "次日留存"
              when t3.day_value = 3 then "三日留存"
              when t3.day_value = 7 then "七日留存"
        else "其他"
        end)

 

 

解题思路:

留存是指用户用户从首次登陆以后再有登陆就算留存下来,不同时长的留存表示这么时长以后仍会再次登陆,比如三日登陆表示用户自首次登陆以后第三天也会进行登陆。我们现在要计算不同留存时长的用户数,首先需要计算不同用户的留存时长,可以用该用户的最后一次登陆时间与首次登陆时间做差就是该用户的留存时长,然后再对留存时长进行分组聚合就得到了我们想要的不同留存时长的用户数。最后运行结果如下:

type uid_cnt
三日留存 1
七日留存 1
次日留存 1

18 筛选最受欢迎的课程

现在有一张学生科目表course_table,这张表存储了每一位学生的id、name(姓名)、grade(年级)、course(选修课程)以及一些其他信息,现在我们想知道哪门课被学生选的人数最多?

course_table表如下所示:

id name grade course
1 王小凤 一年级 心理学
2 刘诗迪 二年级 心理学
3 李思雨 三年级 社会学
4 张文华 一年级 心理学
5 张青云 二年级 心理学
6 徐文杰 三年级 计算机
7 李智瑞 一年级 心理学
8 徐雨秋 二年级 计算机
9 孙皓然 三年级 社会学
10 李春山 一年级 社会学

自己先想一下代码怎么写,然后再参考我的代码。

select 
    course
    ,count(id) as stu_num
from
    demo.course_table
group by
    course
order by
    count(id) desc
limit 1

 

 

解题思路:

我们是要获取被选人数最多的课程,首先需要对课程进行分组,使用的是group by;然后再对组内人数进行计数,即选择该课程的人数,使用的count;然后再对课程人数进行降序排列,使用的是order by;最后把排在第一的课程筛选出来,就是我们要的被选择人数最多的课程。最后结果如下:

course stu_num
心理学 5

想一下上面这种思路是否有问题呢?如果要是有两门或者多门课程的选择人数一样多的时候上面的这种思路得出来的结果是否还正确呢?显然是不正确的。

现在再想一下,如果有多门课程选择人数一样多时怎么办?先想一下再看我的思路。

select 
    course
    ,count(id) as stu_num
from
    demo.course_table
group by
    course
having 
    count(id) = (select 
                 max(stu_num)
                from
                 (select 
                        course
                        ,count(id) as stu_num
                    from
                        demo.course_table
                    group by
                        course
                    )a
                )

 

 

解题思路:

如果存在被选择一样多的课程,我们要把一样多的课程全部筛选出来。首先我们还是需要把每门课程以及被选择的人数获取出来,获取思路与第一种思路是一样的,也是针对课程进行group by,然后再针对组内的人数进行计数;不同点在于最多人数获取上。第一种思路是默认选择人数最多的课程只有一个,而第二种思路是假设选择人数最多的课程有多个时,我们就需要把选择人数最多的人数算出来,这里利用子查询去生成;最后再利用having对分组后的结果进行筛选,从而得到选择人数最多的课程。

19 筛选出每个年级最受欢迎的三门课程

还是前面的course_table,现在我们想知道每个年级被选择最多的三门课程,该怎么实现呢?

自己先想一下代码怎么写,然后再参考我的代码。

select 
    *
from
    (select
        grade
        ,course
        ,stu_num
        ,row_number() over(partition by grade order by stu_num desc) as course_rank
    from
        (select
            grade
            ,course
            ,count(id) as stu_num
        from
            demo.course_table
        group by
            grade
            ,course
        )a
    )b
where 
    b.course_rank < 4

 

 

解题思路:这是典型的获取组内排名的问题,我们前面的一个问题是获取报名人数最多的课程,只需要把每门课程的报名人数获取到,然后把最多的一个取出来就是我们想要的。可是现在这个问题不仅要获取最多的,还要获取第二多、第三多的。而且还是每个年级内的第一、第二、第三多。对于这种问题,我们可以使用窗口函数来实现,先生成每门课程的报名人数,然后再利用row_number()生成每个年级内每门课程的排序结果,最后再通过排序结果筛选出我们需要的排序。最后运行结果如下:

grade course stu_num course_rank
一年级 心理学 3 1
一年级 社会学 1 2
三年级 社会学 2 1
三年级 计算机 1 2
二年级 心理学 2 1
二年级 计算机 1 2

当然,我们这里可以通过where条件筛选任意排名的课程。比如如果要筛选排名第5-8的课程,只需要让where条件中的b.course_rank between 5 and 8即可。

20 求累积和

现在有一张2019年一整年的订单表consum_order_table,consum_order_table包含order_id(订单id)、uid(用户id)、amount(订单金额),现在我们想看下80%的订单金额最少是由多少用户贡献的,该怎么实现呢?

consum_order_table表如下所示:

order_id uid amount
201901 1 10
201902 2 20
201903 3 15
201904 3 15
201905 4 20
201906 4 20
201907 5 25
201908 5 25
201909 6 30
201910 6 30
201911 7 35
201912 7 35

自己先想一下代码怎么写,然后再参考我的代码。

select
    count(uid)
from
    (select
        uid
        ,amount
        ,sum(amount) over(order by amount desc) as consum_amount
        ,(sum(amount) over(order by amount desc))
        /(select sum(amount) from demo.consum_order_table) as consum_amount_rate
    from
        (select
            uid
            ,sum(amount) amount
        from
            demo.consum_order_table
        group by 
            uid
        )
    uid_table)t
where 
    t.consum_amount_rate < 0.8

 

 

解题思路:

我们要获取人80%的订单金额最少由多少用户贡献的,因为我们现在只有一个订单明细表,所以我们需要先生成一个人维度的订单金额表,然后再在这个人维度表的基础上去进行累积和,累计和的实现可以通过窗口函数来实现,这样就可以得到人维度的累积订单金额,在生成累积和的时候需要按照订单金额进行降序排列,这样就可以得到最少的人数,最后再利用一个子查询,获取到全部的订单金额,用累积订单金额去除全部订单金额,就可以得到累积的订单金额贡献情况。最后运行结果如下:

count(uid)
4

sql server 常见面试题

上一篇:[力扣刷题总结](字典树篇)


下一篇:斜率优化总结