3个常考的SQL数据分析题(含数据和代码)

在数据类岗位招聘过程中,经常会考察求职者的SQL能力,这里整理了3个常考的SQL数据分析题,按照由简单到复杂排序,一起来测试一下你掌握了么?

PS:以下SQL代码在MySQL8.0及其以上版本运行。

题目1找出每个部门工资第二高的员工

现有一张公司员工信息表employee,表中包含如下4个字段。

  • employee_id(员工ID):VARCHAR。

  • employee_name(员工姓名):VARCHAR。

  • employee_salary(员工薪资):INT。

  • department(员工所属部门ID):VARCHAR。

employee表的数据如下表所示。

8015dd36a12c4b74a19954ae3f11dd25.png

还有一张部门信息表department,表中包含如下两个字段。

  • department_id(部门ID):VARCHAR。

  • department_name(部门名称):VARCHAR。

department表的数据如下表所示。

090a19ac0abef4ac659421ab5504ca5b.png

数据导入的代码如下:

DROP TABLE IF EXISTS employee;
CREATE TABLE employee(
employee_id VARCHAR(8),
employee_name VARCHAR(8),
employee_salary INT(8),
department VARCHAR(8)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
INSERT INTO
employee (employee_id,employee_name,employee_salary,department) 
VALUE ('a001','Bob',7000,'b1')
     ,('a002','Jack',9000,'b1')
     ,('a003','Alice',8000,'b2')
     ,('a004','Ben',5000,'b2')
     ,('a005','Candy',4000,'b2')
     ,('a006','Allen',5000,'b2')
     ,('a007','Linda',10000,'b3');




DROP TABLE IF EXISTS department;
CREATE TABLE department(
department_id VARCHAR(8),
department_name VARCHAR(8)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
INSERT INTO
department (department_id,department_name) 
VALUE ('b1','Sales')
     ,('b2','IT')
     ,('b3','Product');

问题:查询每个部门薪资第二高的员工信息。

输出内容包括:

  • employee_id(员工ID)

  • employee_name(员工姓名)

  • employee_salary(员工薪资)

  • department_id(员工所属部门名称)

结果样例如下图所示。

b99b34aef682382bedbafb8a9c422688.png

可供参考的解题思路:使用窗口函数根据部门ID分组,在组内按照员工薪资降序排列并记为ranking,然后将该处理后的表和部门信息表进行内连接,从而把部门名称关联进来,最后在连接后的表上使用ranking=2作为薪资第二高的条件进行WHERE筛选,选择需要的列,即可得到结果。

涉及知识点:窗口函数、子查询、多表连接。

本题的SQL代码如下,供读者参考:

SELECT  a.employee_id
       ,a.employee_name
       ,a.employee_salary
       ,b.department_id
FROM 
(
    SELECT  *
           ,RANK() OVER (PARTITION BY department ORDER BY employee_salary DESC) AS ranking
    FROM employee 
) AS a
INNER JOIN department AS b
ON a.department = b.department_id
WHERE a.ranking = 2;

题目2:网站登录时间间隔统计

现有一张网站登录情况表login_info,该表记录了所有用户的网站登录信息,包含如下两个字段。

  • user_id(用户ID):VARCHAR。

  • login_time(用户登录日期):DATE。

login_info表的数据如下表所示。

b6664e361c3d7f56eaa7ebc8fd146557.png

数据导入的代码如下:

DROP TABLE IF EXISTS login_info;
CREATE TABLE login_info(
user_id VARCHAR(8),
login_time DATE
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
INSERT INTO
login_info (user_id,login_time) 
VALUE ('a001','2021-01-01')
,('b001','2021-01-01')
,('a001','2021-01-03')
,('a001','2021-01-06')
,('a001','2021-01-07')
,('b001','2021-01-07')
,('a001','2021-01-08')
,('a001','2021-01-09')
,('b001','2021-01-09')
,('b001','2021-01-10')
,('b001','2021-01-15')
,('a001','2021-01-16')
,('a001','2021-01-18')
,('a001','2021-01-19')
,('b001','2021-01-20')
,('a001','2021-01-23');

问题:计算每个用户登录日期间隔小于5天的次数。

输出内容包括:

  • user_id(用户ID)

  • num(用户登录日期间隔小于5天的次数)

结果样例如下图所示。

28b4919f79e195a7a3454822ddc525a8.png

可供参考的解题思路:本题考查LEAD()函数在处理时间间隔问题上的使用方法,观察内层的查询部分,使用LEAD()函数在原有的login_time字段的基础上创造一列新的时间字段(即该用户下一次登录日期),内层查询代码如下:

SELECT  user_id 
       ,login_time 
       ,LEAD(login_time,1) OVER (PARTITION BY user_id ORDER BY login_time) AS next_login_time
FROM login_info;

查询结果如下图所示。

cc344a135a5990e5e0df63b3f566b053.png

在上图中可以发现,经过LEAD()函数处理后,数据会根据user_id字段分组后按照login_time字段排序。经过内层的处理后,只需在外层筛选出next_login_time与login_time字段的日期差小于5天的数据,即最终统计的目标数据,这里使用了TIMESTAMPDIFF(DAY, login_time, next_login_time)计算日期差,最后分组聚合统计不同user_id的记录个数,即每个用户登录日期间隔小于5天的次数。

涉及知识点:窗口函数、子查询、分组聚合、时间函数。

本题的SQL代码如下,供读者参考:

SELECT  a.user_id
       ,COUNT(*) AS num
FROM 
(
    SELECT  user_id
           ,login_time
           ,LEAD(login_time,1) OVER (PARTITION BY user_id ORDER BY login_time) AS next_login_time
    FROM login_info
) AS a
WHERE TIMESTAMPDIFF(DAY, login_time, next_login_time) < 5 
GROUP BY user_id;

题目3:用户购买渠道分析

现有一张用户购买信息表purchase_channel,该表记录了用户在某购物平台的购物信息,该购物平台具有网页端(web)和手机端(app)两种访问方式,表中包含如下4个字段。

  • user_id(用户ID):VARCHAR。

  • channel(用户购买渠道):VARCHAR。

  • purchase_date(购买日期):DATE。

  • purchase_amount(购买金额):INT。

purchase_channel表的数据如下表所示。

02abc648216c8b83092eee308ea12a9a.png

数据导入代码如下:

DROP TABLE IF EXISTS purchase_channel;
CREATE TABLE purchase_channel(
user_id VARCHAR(8),
channel VARCHAR(8),
purchase_date DATE,
purchase_amount INT(8)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
INSERT INTO
purchase_channel (user_id,channel,purchase_date,purchase_amount) 
VALUE ('a001','app','2021-03-14',200)
     ,('a001','web','2021-03-14',100)
     ,('a002','app','2021-03-14',400)
     ,('a001','web','2021-03-15',3000)
     ,('a002','app','2021-03-15',900)
     ,('a003','app','2021-03-15',1000);

问题:查询每天仅使用手机端的用户、仅使用网页端的用户和同时使用网页端和手机端(both)的不同用户人数和总购物金额,并且即使某天某渠道没有用户的购买信息,也需要展示。

输出内容包括:

purchase_date(日期)

channel(购买渠道)

sum_amount(总购买金额)

total_users(不同用户人数)

结果样例如下图所示。

c626f3eea9a673ba39f709339fd3765e.png

可供参考的解题思路:根据用户ID和日期进行分组,通过统计用户在各购买渠道购物的记录个数来判断某用户在某日期购物时采用的访问方式(web、app和both)。其中,web和app可以通过一个SELECT语句查询,both则可以通过另一个SELECT语句查询。将两部分使用UNION连接在一起,并将以上部分作为子查询内部,在子查询外部统计不同购买日期、购买渠道的总购买金额和总购买用户。

本部分SQL代码如下:

SELECT  purchase_date 
       ,channel 
       ,SUM(sum_amount) sum_amount 
       ,SUM(total_users) total_users
FROM 
(
    SELECT  purchase_date 
           ,MIN(channel) channel 
           ,SUM(purchase_amount) sum_amount 
           ,COUNT(DISTINCT user_id) total_users
    FROM purchase_channel
    GROUP BY  purchase_date
             ,user_id
    HAVING COUNT(DISTINCT channel) = 1 UNION
    SELECT  purchase_date 
           ,'both' channel 
           ,SUM(purchase_amount) sum_amount 
           ,COUNT(DISTINCT user_id) total_users
    FROM purchase_channel
    GROUP BY  purchase_date
             ,user_id
    HAVING COUNT(DISTINCT channel) > 1 
) c
GROUP BY  purchase_date
         ,channel;

本部分输出结果如下图所示。

5270bf269e6d9f9d188c9d43ddcb8539.png

上述部分似乎已经完成了本题要求,但仔细观察就会发现,题目要求即使某天某渠道没有用户的购买信息,也需要展示。而想要展示更全的信息,则考虑使用最全的信息(所有日期和3个渠道的笛卡尔积)与刚查询出的结果数据表进行LEFT JOIN连接,即可得到两张表根据日期和渠道进行连接的结果。

涉及知识点:UNION、分组聚合、数据去重。

本题的SQL代码如下,供读者参考:

SELECT  t1.purchase_date
       ,t1.channel
       ,t2.sum_amount
       ,t2.total_users
FROM 
(
    SELECT  DISTINCT a.purchase_date 
           ,b.channel
    FROM purchase_channel a, 
    (
        SELECT  "app" AS channel 
        UNION
        SELECT  "web" AS channel 
        UNION
        SELECT  "both" AS channel 
    ) b
) t1
LEFT JOIN 
(
SELECT 
purchase_date,
channel,
SUM(sum_amount) sum_amount,
SUM(total_users) total_users
FROM (
SELECT  purchase_date 
           ,MIN(channel) channel 
           ,SUM(purchase_amount) sum_amount 
           ,COUNT(DISTINCT user_id) total_users
    FROM purchase_channel
    GROUP BY  purchase_date,user_id
    HAVING COUNT(DISTINCT channel) = 1 
    UNION
    SELECT  purchase_date 
           ,'both' channel 
           ,SUM(purchase_amount) sum_amount 
           ,COUNT(DISTINCT user_id) total_users
    FROM purchase_channel
    GROUP BY  purchase_date,user_id
    HAVING COUNT(DISTINCT channel) > 1
)c GROUP BY purchase_date, channel
) t2
ON t1.purchase_date = t2.purchase_date AND t1.channel = t2.channel;

8584ae79f61e933d3abbb241cb7ba9d0.gif

这些题目你做出了么?

本文题目内容摘自最新出版的《SQL数据分析:从基础破冰到面试题解》,该书包含大量练习题(共48个),并附带数据导入和参考解析代码,更多题目见下图:

b44cac68a46e256f13992b43db412a0f.png

6e463aa1d0fffa5085d1403f582162be.png

常见疑问和解答

1. 没有基础可以学吗?

可以,本书从环境搭建开始,从基础入门到进阶,然后通过题目实战提升SQL能力,是一本关于SQL 数据分析的实战手册。

2. 本书对比市面上大部分SQL书籍的特色是什么?

将数据分析挖掘中所需的SQL能力抽出来专门写,不会涉及很多不常用的功能语法,由浅入深,并配套大量练习题(可作为求职笔试面试的练习题),每个练习题都配有数据导入、解题思路和参考答案。练习题会结合当前数据分析很多场景需求来编制,例如“活跃用户分析”、“连续登录用户分析”、“社区团购行为分析”、“商品销量同环比”。

3. 本书适合数据分析相关岗位求职备考准备么?

很适合,本书展现了数据分析工作的日常内容,给出了数据分析岗位的工作技能要求,然后讲述了数据分析笔试与面试中对SQL 的考查知识点。通过3种难度的题目练习,能提升求职能力并达到初级数据分析挖掘岗位对SQL的能力要求。

4. 本书的适合什么样的读者?

  • 数据分析与数据开发求职者和从业者

  • 计算机科学与技术、统计学、数学、大数据、人工智能、数据科学相关专业的师生

  • 对数据分析和SQL感兴趣人群

  • 转行做数据分析与数据开发的人员

16a6ca653846576822011b65d16daf58.gif

福利

大家如果经常买书都会知道,新书刚出版上市是没有什么折扣的。

本次给大家申请到了全网最低价福利,原价89元,现在只要49元包邮

大家可以扫描下方二维码或者点击阅读原文直接购买。

908bce52c05098c3c038d1561df808bf.png

老规矩,还是免费送大家几本,给本篇文章同时点赞和点在看,然后给我微信发送“已点在看”,并附上截图,我会随机抽取5名,各送《SQL数据分析》新书一本。周二(1.4)晚9:00开奖。

7b18fd8f5d0cfafe09c2e67397aab2a6.png

我的个人微信(没加的加一下)

60d460d783ae577c03604677bfa7c76a.png

点分享

e8f83d00bd469caca3e3af577f9a2ed4.png

点收藏

c896b3e41a1af70a60cbf9d80d8b92e7.png

点点赞

0651322e75aee6ebf42a95eb87b06cb5.png

点在看

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值