学习笔记:SQL大厂面试题集

题源代码来自:​​​​​​数据分析-全网最全SQL面试真题(做完,面试再也不虚了-划分了知名互联网大厂、不同行业背景(如电商、游戏)、常考知识点三大部分的SQL真题)_哔哩哔哩_bilibili欢迎大家一起交流学习,课件资料会在数据蛙SQL面试题交流群持续更新~加群方式-加V:lkx941013,备注:SQL视频持续更新inghttps://www.bilibili.com/video/BV1Rh411B7sN?share_source=copy_web【724补】过好久了,再重新整理复习一下| ᴥ•́ )✧

目录

一、货拉拉面试题-用户取消率

二、小红书面试题-用户行为分析

三、 滴滴面试题(数据太多待补)

四、网易面试题-用户复购率(数据待补)

五、支付宝面试题-求支付金额在前20%的用户(数据待补)

六、中金财富证券互联网金融部面试题(数据待补)

七、游戏类面试题

八、电商类面试题


一、货拉拉面试题-用户取消率

先建表导入数据

CREATE TABLE IF NOT EXISTS trips (
id VARCHAR(255),
client_id VARCHAR(255),
driver_id VARCHAR(255),
city_id VARCHAR(255),
STATUS VARCHAR(255),
request_at VARCHAR(255));
INSERT INTO trips VALUES('1','1','10','1','completed','2019-10-01');
INSERT INTO trips VALUES('2','2','11','1','cancelled_by_driver','2019-10-01');
INSERT INTO trips VALUES('3','3','12','6','completed','2019-10-01');
INSERT INTO trips VALUES('4','4','13','6','cancelled_by_driver','2019-10-01');
INSERT INTO trips VALUES('5','1','10','1','completed','2019-10-02');
INSERT INTO trips VALUES('6','2','11','6','completed','2019-10-02');
INSERT INTO trips VALUES('7','3','12','6','completed','2019-10-02');
INSERT INTO trips VALUES('8','2','12','12','completed','2019-10-03');
INSERT INTO trips VALUES('9','3','10','12','completed','2019-10-03');
INSERT INTO trips VALUES('10','4','13','12','cancelled_by_driver','2019-10-03');


CREATE TABLE IF NOT EXISTS users (
users_id VARCHAR(255),
banned VARCHAR(255),
ROLE VARCHAR(255));
INSERT INTO  users VALUES('1','no','client');
INSERT INTO  users VALUES('2','yes','client');
INSERT INTO  users VALUES('3','no','client');
INSERT INTO  users VALUES('4','no','client');
INSERT INTO  users VALUES('10','no','driver');
INSERT INTO  users VALUES('11','no','driver');
INSERT INTO  users VALUES('12','no','driver');
INSERT INTO  users VALUES('13','no','driver');

SELECT * FROM trips;
SELECT * FROM users;

 求2019-10-01到03期间,非被禁止用户取消率,保留两位小数。

学习笔记:SQL大厂面试题集_第1张图片学习笔记:SQL大厂面试题集_第2张图片

 用户取消率=用户取消数/用户数=SUM(IF(STATUS='completed',0,1))/COUNT(*)由于取消情况较复杂,有乘客取消和司机取消,所以采用反集的概念,若判断订单完成,输出0,否则为1.

SELECT request_at,COUNT(*),SUM(IF (STATUS='completed', 0 , 1)),ROUND(SUM(IF (STATUS='completed', 0 , 1))/COUNT(*),2)a
FROM trips RIGHT JOIN users ON trips.client_id=users.users_id
WHERE banned='no' AND request_at BETWEEN '2019-10-01' AND'2019-10-03'
GROUP BY request_at;

二、小红书面试题-用户行为分析

有订单事务表、收藏事务表,要求:请用一句sql取出所有用户对商品的行为特征,
特征分为已购买、购买未收藏、收藏未购买、收藏且购买

CREATE TABLE redbk_orders(
id INT,
user_id VARCHAR(10),
item_id INT,
par_time VARCHAR(30),
item_num INT);
INSERT INTO redbk_orders VALUES(1,'001','201','2018-08-31 00:00:01',1);
INSERT INTO redbk_orders VALUES(2,'002','203','2018-09-02 12:00:02',2);
INSERT INTO redbk_orders VALUES(3,'003','203','2018-09-01 00:00:01',1);
INSERT INTO redbk_orders VALUES(4,'003','203','2018-09-04 09:10:30',1);
CREATE TABLE redbk_favorites(
id INT,
user_id VARCHAR(10),
item_id INT,
fav_time VARCHAR(30));
INSERT INTO redbk_favorites VALUES(1,'001','201','2018-08-31 00:00:01');
INSERT INTO redbk_favorites VALUES(2,'002','202','2018-09-02 12:00:02');
INSERT INTO redbk_favorites VALUES(3,'003','204','2018-09-01 00:00:01');

解答思路:
1.将user_id和item_id两个主键进行关联;
2.根据是否关联上可以知道是否购买和收藏;
3.full JOIN 全外连接可以得到结果,但mysql要使用union来实现(union all不去重)。

SELECT o.user_id,o.item_id,
1 AS '已购买',
CASE WHEN f.item_id IS NULL THEN 1 ELSE 0 END AS '购买未收藏',
0 AS '收藏未购买',
CASE WHEN f.item_id IS NOT NULL THEN 1 ELSE 0 END AS '收藏且购买'
FROM redbk_orders o LEFT JOIN redbk_favorites f
ON o.user_id=f.user_id AND o.item_id=f.item_id
UNION
SELECT f.user_id,f.item_id,
CASE WHEN o.item_id IS NOT NULL THEN 1 ELSE 0 END AS '已购买',
0 AS '购买未收藏',
CASE WHEN o.item_id IS NULL THEN 1 ELSE 0 END AS '收藏未购买',
CASE WHEN o.item_id IS NOT NULL THEN 1 ELSE 0 END AS '收藏且购买'
FROM redbk_favorites f
LEFT JOIN redbk_orders o ON o.user_id=f.user_id AND o.item_id=f.item_id
ORDER BY user_id,item_id;

用到了CASE WHEN  条件 THEN 成立结果 ELSE 失败结果 END

 学习笔记:SQL大厂面试题集_第3张图片

三、 滴滴面试题(数据太多待补)

提取2020年8月和9月,每个月北京市新老司机(首单日期在当月为新司机)的司机数、在线时长和TPH(订单量/在线时长)数据。
解题思路:
a.关键在于如何对新老司机做查询
新老司机:用IF做判断,若当日日期=首次完成订单时间,为新司机,否则为老司机。
代码如下:

SELECT a.*,IF(DATE_FORMAT(DATE,'%Y-%m')=DATE_FORMAT(a.'首次完成订单时间','%Y-%m'),'新司机','老司机')AS 新老司机
FROM '司机数据'a
WHERE (a.'日期' BETWEEN '2020-08-01' AND '2020-09-30') AND a.'城市id'='100000';

 b.最终代码
思考:为何不联结表一起计算新老司机数量、订单量、在线时长?
1,由于司机一天内可能会接多个订单,若直接关联,司机id将不唯一,会导致老司机列计数出现重复,计数结果不准确;
2,在线时长表中没给出每个订单所花时间,只给出当天所有订单共花时间。

WITH temp1 AS(#求新老司机数量
SELECT t.'城市id', MONTH(t.'日期')AS 月份,
SUM(IF(新老司机='新司机',1,0))AS 新司机数,
SUM(IF(新老司机='老司机',1,0))AS 老司机数
FROM
(SELECT a.*,IF(DATE_FORMAT(DATE,'%Y-%m')=DATE_FORMAT(a.'首次完成订单时间','%Y-%m'),
'新司机','老司机')AS 新老司机
FROM '司机数据'a
WHERE (a.'日期' BETWEEN '2020-08-01' AND '2020-09-30') AND a.'城市id'='100000';)AS t
GROUP BY MONTH(t.'日期')),
temp2 AS(#求订单量
SELECT MONTH(a.'日期')AS 月份,COUNT(a.'订单id')AS 订单量
FROM '订单数据'a
LEFT JOIN '司机数据'b ON a.司机id=b.司机id
WHERE (a.'日期'BETWEEN '2020-08-01' AND '2020-09-30')AND b.'城市id'='100000'
GROUP BY MONTH(a.'日期')),
temp3 AS(#求在线时长
SELECT MONTH(a.'日期')AS 月份,SUM(a.'在线时长')AS 在线时长
FROM '在线时长数据'a
LEFT JOIN '司机数据'b ON a.司机id=b.司机id
WHERE (a.'日期'BETWEEN '2020-08-01' AND '2020-09-30')AND b.'城市id'='100000'
GROUP BY MONTH(a.'日期'))
SELECT temp1.城市id,temp1.月份,temp1.新司机数,temp1.老司机数,temp2.订单量,temp3.在线时长,temp2.订单量/temp3.在线时长 TPH
FROM temp1
INNER JOIN temp2
ON temp1.月份=temp2.月份
INNER JOIN temp3
ON temp3.月份=temp2.月份

①这里补充日期格式化函数DATE_FORMAT()

%Y-%m-%d  2017/4/30

%y-%m-%d  17/4/30   区分大小写,其他参考:DATE_FORMAT函数用法_Lambdas的博客-CSDN博客_date_format

②每一步用temp临时表储存,最后联结查找。

四、网易面试题-用户复购率(数据待补)

1.查出每个用户第一单的消费金额
方法一:使用窗口函数ROW_NUMBER()OVER()

这里补充:1.rank函数:计算排序时,如果存在相同位次的记录,则会跳过之后的位次 1,2,2,4;

                  2.dense_rank函数:即使存在相同位次的记录,也不会跳过之后的位次 1,2,2,3;

                  3.row_number函数:赋予唯一的连续位次 1,2,3,4。

SELECT a.userid,a.amount
FROM (SELECT*,ROW_NUMBER()OVER(PARTITION BY userid ORDER BY paytime)AS rn FROM netease_order)AS a
WHERE a.rn=1;

 方法二:使用变量

SELECT b.*
FROM(SELECT a.*,CASE WHEN @userid=userid THEN @rh=rh+1
                WHEN @userid:=userid THEN @rh:=1 END AS rn
     FROM netease_order a,(SELECT @userid:=null,@rh:=0)t ORDER BY a.userid,paytime) b
WHERE b.rh=1
ORDER BY b.userid

 这里@表示变量   :表示赋值

方法三:使用分组求最小值

SELECT t.userid,t.first_time,b.amount
FROM(SELECT userid,MIN(paytime)AS first_time FROM netease_order GROUP BY userid)t
INNER JOIN netease_order b ON t.userid=b.userid AND t.first_time=b.paytime;

2.查出每个月的新客数(新客指在严选首次支付的用户),当月有复购的新客数,新客当月复购率(当月有复购的新客数/月新客数)

WITH temp1 AS(#1.找出每个用户第一次下单的时间和年月
SELECT userid,MIN(paytime),DATE_FORMAT(paytime,'%Y-%m')'年月'
FROM netease_order GROUP BY userid),
temp2 AS(#2.找出每月的新客户数目
SELECT userid,年月,COUNT(userid)AS'新客户数'
FROM temp1 GROUP BY 年月),
temp3 AS(#3.1 找出当月有复购的新客户数,第一步找出用户在第一次下单的那个月的购买次数
SELECT n.userid,COUNT(n.orderid)AS'次数',年月
FROM netease_order n
INNER JOIN temp1 ON  n.userid=temp1.userid AND temp1.年月=DATE_FORMAT(paytime,'%Y-%m')
GROUP BY temp1.userid),
temp4 AS(#3.2 第二步是将有复购的新客筛选出来(购买次数>=2)
SELECT 年月,temp3.userid,COUNT(temp3.userid)AS'有复购的新客户数目'
FROM temp3 WHERE 次数>=2 GROUP BY temp3.年月),
temp5 AS(#计算复购率
SELECT temp2.年月,temp2.新客户数,IFNULL(temp4.有复购的新客户数目,0),IFNULL(temp4.有复购的新客户数目/temp2.新客户数,0) 复购率
FROM temp2 LEFT JOIN temp4 ON temp2.年月=temp4.年月)
SELECT * FROM temp5;

五、支付宝面试题-求支付金额在前20%的用户(数据待补)

思路:借助窗口函数ntile将每个用户和对应的支付金额分成5组(这样每组就有1/5),取分组排名第一的用户组即前支付金额在前20%。

SELECT t.user_name,t.all_pay_amount
FROM(SELECT user_name,SUM(pay_amount)all_pay_amount,NTILE(5) OVER (ORDER BY SUM(pay_amount) DESC) n FROM user_sales_table GROUP BY user_name)t
WHERE t.n=1;

六、中金财富证券互联网金融部面试题(数据待补)

统计:每个客户,在签约订单之前(不包括签约当日),在每个页面的访问次数,在每个点击事件的触发次数。
提示说明:
①所统计的是:客户在签约(购买)投顾服务订单之前所访问的各个页面的次数+在客户在签约(购买)投顾服务订单之前所点击的各个事件的次数;
②某人的页面访问次数的统计:在页面访问表(page_visit_test_201909)中的任意一条数据,代表某客户(“客户编号”字段)在某日(“页面访问的时间”字段)访问了某页面(“访问的页面”字段)1次,计数为1;
③某人的点击事件次数的统计:在点击事件表(event_visit_test_201909)中的任意一条数据,代表某客户(“客户编号”字段)在某日(“事件发生时间”字段)点击了某事件(“事件名称”字段)1次,计数为1;
④最终将每个客户访问各页面的次数和点击各事件的次数同时展示在一个结果表中,展示形式如下:

客户编号 找投顾 选组合 投顾观点详情页 投顾观点评论页 投顾观点列表

投顾

个人页

投顾组合评论页 投顾组合详情页

互联网

投顾首页

是否购买
30247447 1 1 1 0 0 1 0 0 0 1
10177017 2 1 2 0 0 0 0 0 0 1

⑤最后一个字段“是否购买”的判断条件是:如果在订单数据中能够找到对应的客户(编号),且“组合名称”不为空,则代表该客户签约(购买)了投顾服务(产品);
⑥如遇到重复购买的人,则第一次购买前的行为数据为:9月1日至第一次购买前的行为数据(统计);第二次购买前的行为数据为:第一次购买和第二次购买之间用户行为数据(统计);以此类推。

原始数据 三张表:页面访问数据,点击事件触发数据,订单数据

可以先进行一些数据查看这里没有数据略过

筛选出在9月份之后真正签约的客户

SELECT *FROM order_test_201909
WHERE 签约日期>='2019-09-01'AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期;

区分复购和非复购的用户

SELECT*,ROW_NUMBER()OVER(PARTITION BY 客户编号 ORDER BY 签约日期) rn
FROM(SELECT * FROM order_test_201909 WHERE 签约日期>='2019-09-01' AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期) t
ORDER BY 客户编号,签约日期

第一次购买前的行为数据为:9月1日至第一次购买前的行为数据(统计)

SELECT*FROM(
SELECT*,ROW_NUMBER()OVER(PARTITION BY 客户编号 ORDER BY 签约日期) rn FROM(SELECT * FROM order_test_201909 WHERE 签约日期>='2019-09-01' AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期) t ORDER BY 客户编号,签约日期) o
INNER JOIN page_visit_test_201909 p ON o.客户编号=p.客户编号
WHERE rn=1

行转列

SELECT o.客户编号,
SUM(IF(访问的页面='找投顾' AND 页面访问时间<签约日期,1,0)) AS 找投顾,
SUM(IF(访问的页面='选组合' AND 页面访问时间<签约日期,1,0)) AS 选组合,
SUM(IF(访问的页面='投顾观点详情页' AND 页面访问时间<签约日期,1,0)) AS 投顾观点详情页,
SUM(IF(访问的页面='投顾观点评论页' AND 页面访问时间<签约日期,1,0)) AS 投顾观点评论页,
SUM(IF(访问的页面='投顾观点列表' AND 页面访问时间<签约日期,1,0)) AS 投顾观点列表,
SUM(IF(访问的页面='投顾个人页' AND 页面访问时间<签约日期,1,0)) AS 投顾个人页,
SUM(IF(访问的页面='投顾组合评论页' AND 页面访问时间<签约日期,1,0)) AS 投顾组合评论页,
SUM(IF(访问的页面='投顾组合详情页' AND 页面访问时间<签约日期,1,0)) AS 投顾组合详情页,
SUM(IF(访问的页面='互联网投顾首页' AND 页面访问时间<签约日期,1,0)) AS 互联网投顾首页,
1 AS 是否购买
FROM(SELECT*,ROW_NUMBER()OVER(PARTITION BY 客户编号 ORDER BY 签约日期) rn FROM(SELECT * FROM order_test_201909 WHERE 签约日期>='2019-09-01' AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期) t ORDER BY 客户编号,签约日期) o
INNER JOIN page_visit_test_201909 p ON o.客户编号=p.客户编号
WHERE rn=1 GROUP BY o.客户编号

第二次购买前的行为数据为:第一次购买和第二次购买之间用户行为数据(统计)
添加字段,订单1的签约日期,订单2的签约日期

SELECT*,IF(rn=1,签约日期,NULL)第一次签约日期,LAG(IF(rn=1,签约日期,NULL),1)OVER(ORDER BY 客户编号,签约日期)第一次签约日期_,IF(rn=2,签约日期,NULL)第二次签约日期
FROM(SELECT*,ROW_NUMBER()OVER(PARTITION BY 客户编号 ORDER BY 签约日期) rn FROM(SELECT * FROM order_test_201909 WHERE 签约日期>='2019-09-01' AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期) t
ORDER BY 客户编号,签约日期) t1

LAG(IF(rn=1,签约日期,NULL),1)OVER(ORDER BY 客户编号,签约日期)第一次签约日期_   可返回第一次签约日期列前一行的数据。视觉上是下移一位

补充:LEAD(field, num, defaultvalue) field 需要查找的字段,num 往查找的 num 行的数据,defaultvalue 没有符合条件的默认值

LAG(field, num, defaultvalue) field 需要查找的字段,num 往查找的 num 行的数据,defaultvalue 没有符合条件的默认值

第一次购买和第二次购买之间用户的行为特征数据

SELECT o.客户编号,
SUM(IF(访问的页面='找投顾' AND 页面访问时间<签约日期,1,0)) AS 找投顾,
SUM(IF(访问的页面='选组合' AND 页面访问时间<签约日期,1,0)) AS 选组合,
SUM(IF(访问的页面='投顾观点详情页' AND 页面访问时间<签约日期,1,0)) AS 投顾观点详情页,
SUM(IF(访问的页面='投顾观点评论页' AND 页面访问时间<签约日期,1,0)) AS 投顾观点评论页,
SUM(IF(访问的页面='投顾观点列表' AND 页面访问时间<签约日期,1,0)) AS 投顾观点列表,
SUM(IF(访问的页面='投顾个人页' AND 页面访问时间<签约日期,1,0)) AS 投顾个人页,
SUM(IF(访问的页面='投顾组合评论页' AND 页面访问时间<签约日期,1,0)) AS 投顾组合评论页,
SUM(IF(访问的页面='投顾组合详情页' AND 页面访问时间<签约日期,1,0)) AS 投顾组合详情页,
SUM(IF(访问的页面='互联网投顾首页' AND 页面访问时间<签约日期,1,0)) AS 互联网投顾首页,
1 AS 是否购买
FROM(SELECT*,IF(rn=1,签约日期,NULL)第一次签约日期,LAG(IF(rn=1,签约日期,NULL),NULL,1)OVER(ORDER BY 客户编号,签约日期)第一次签约日期_,IF(rn=2,签约日期,NULL)第二次签约日期
     FROM(SELECT*,ROW_NUMBER()OVER(PARTITION BY 客户编号 ORDER BY 签约日期) rn FROM(SELECT * FROM order_test_201909 WHERE 签约日期>='2019-09-01' AND 组合名称 IS NOT NULL ORDER BY 客户编号,签约日期) t ORDER BY 客户编号,签约日期) t1)o
INNER JOIN page_visit_test_201909 p ON o.客户编号=p.客户编号
WHERE rn=2 AND 页面访问时间 BETWEEN 第一次签约日期_ AND 第二次签约日期 GROUP BY o.客户编号;

七、游戏类面试题

1.将数据生产年、月、及对应每月实际数据(题中数据为累加,例如需统计2016年2月数据,需要将1月份扣减)
数据准备:
创建promotion_data表

CREATE TABLE promotion_data(
log_data VARCHAR(255),
accumulated_impressions VARCHAR(255));
插入数据
INSERT INTO promotion_data VALUES('2015-12-31','900');
INSERT INTO promotion_data VALUES('2016-01-01','1000');
INSERT INTO promotion_data VALUES('2016-01-02','1100');
INSERT INTO promotion_data VALUES('2016-01-03','1200');
INSERT INTO promotion_data VALUES('2016-01-04','1300');
INSERT INTO promotion_data VALUES('2016-01-05','1400');
INSERT INTO promotion_data VALUES('2016-01-06','1500');
INSERT INTO promotion_data VALUES('2016-01-07','1600');
INSERT INTO promotion_data VALUES('2016-01-08','1700');
INSERT INTO promotion_data VALUES('2016-01-09','1800');
INSERT INTO promotion_data VALUES('2016-01-10','1900');
INSERT INTO promotion_data VALUES('2016-01-11','2000');
INSERT INTO promotion_data VALUES('2016-01-12','2100');
INSERT INTO promotion_data VALUES('2016-01-13','2200');
INSERT INTO promotion_data VALUES('2016-01-14','2300');
INSERT INTO promotion_data VALUES('2016-01-15','2400');
INSERT INTO promotion_data VALUES('2016-01-16','2500');
INSERT INTO promotion_data VALUES('2016-01-17','2600');
INSERT INTO promotion_data VALUES('2016-01-18','2700');
INSERT INTO promotion_data VALUES('2016-01-19','2800');
INSERT INTO promotion_data VALUES('2016-01-20','2900');
INSERT INTO promotion_data VALUES('2016-01-21','3000');
INSERT INTO promotion_data VALUES('2016-01-22','3100');
INSERT INTO promotion_data VALUES('2016-01-23','3200');
INSERT INTO promotion_data VALUES('2016-01-24','3300');
INSERT INTO promotion_data VALUES('2016-01-25','3400');
INSERT INTO promotion_data VALUES('2016-01-26','3500');
INSERT INTO promotion_data VALUES('2016-01-27','3600');
INSERT INTO promotion_data VALUES('2016-01-28','3700');
INSERT INTO promotion_data VALUES('2016-01-29','3800');
INSERT INTO promotion_data VALUES('2016-01-30','3900');
INSERT INTO promotion_data VALUES('2016-01-31','4000');
INSERT INTO promotion_data VALUES('2016-02-01','4100');
INSERT INTO promotion_data VALUES('2016-02-02','4200');
INSERT INTO promotion_data VALUES('2016-02-03','4300');

 参考答案:

--新增一个字段data2,data2的数据是根据data1向下移动一位得到
SELECT log_data ,accumulated_impressions AS data1,LAG(accumulated_impressions,1)OVER(ORDER BY log_data ASC)data2
FROM promotion_data
--data1-data2获得每天实际新增
SELECT*,DATE_FORMAT(log_data,'%Y-%m')ym,(data1-data2)AS day_increment
FROM(SELECT log_data ,accumulated_impressions AS data1,LAG(accumulated_impressions,1)OVER(ORDER BY log_data ASC)data2 FROM promotion_data)t1
--分组年月,聚合每天实际新增得到每月实际新增
SELECT ym,SUM(day_increment)month_increment
FROM(SELECT*,DATE_FORMAT(log_data,'%Y-%m')ym,(data1-data2)AS day_increment FROM(SELECT log_data ,accumulated_impressions AS data1,LAG(accumulated_impressions,1)OVER(ORDER BY log_data ASC)data2 FROM promotion_data)t1)t2
GROUP BY ym;

学习笔记:SQL大厂面试题集_第4张图片 

2.查询‘<7days’表示此玩家每一行日期的前7天之内玩过游戏
创建play_time表

CREATE TABLE play_time(
play_name VARCHAR(255),
play_date DATE,
play_length INT);
插入数据
INSERT INTO play_time VALUES('cc','2020/1/31',180);
INSERT INTO play_time VALUES('cc','2020/2/1',150);
INSERT INTO play_time VALUES('mb','2020/2/1',120);
INSERT INTO play_time VALUES('nb','2020/2/2',133);
INSERT INTO play_time VALUES('cca','2020/2/3',160);
INSERT INTO play_time VALUES('fg','2020/2/4',154);
INSERT INTO play_time VALUES('mb','2020/2/6',173);
INSERT INTO play_time VALUES('fg','2020/2/12',220);
INSERT INTO play_time VALUES('nb','2020/2/12',220);
INSERT INTO play_time VALUES('cca','2020/2/16',201);
INSERT INTO play_time VALUES('fg','2020/2/23',99);

添加辅助列play_date_

SELECT *,LAG(play_date,1)OVER(PARTITION BY play_name ORDER BY play_date)play_date_
FROM play_time
#判断play_date和play_date_的日期是否小于7天
SELECT play_name,play_date,play_length,CASE WHEN play_date_ IS NULL THEN 'no'
                                            WHEN DATEDIFF(play_date,play_date_)<7 THEN 'yes'ELSE 'no' END '<7 days'
FROM(SELECT *,LAG(play_date,1)OVER(PARTITION BY play_name ORDER BY play_date)play_date_ FROM play_time)t
ORDER BY  play_name,play_date; 

DATEDIFF() 函数返回两个日期之间的时间。更多参考:SQL Server DATEDIFF() 函数学习笔记:SQL大厂面试题集_第5张图片

八、电商类面试题

数据准备

建表
CREATE TABLE trans(
counter_id CHAR(20) COMMENT"柜台ID",
counter_name CHAR(20) COMMENT"柜台名称",
customer_id CHAR(20) COMMENT"客户ID",
order_id CHAR(20) COMMENT"订单ID",
product_id CHAR(20) COMMENT"产品ID",
product_name_cn CHAR(20) COMMENT"产品名称",
quantity INT COMMENT"购买数量",
unit_price DECIMAL(8,1) COMMENT"产品单价",
purchase_date DATE COMMENT"购买时间",
brand CHAR(20) COMMENT"购买品牌");

CREATE TABLE PROFILE(
customer_id CHAR(20) COMMENT"客户ID",
first_pur_date DATE COMMENT"购买时间",
age INT COMMENT"客户年龄");
插入数据
INSERT INTO trans VALUES
('offline001','counterA','customer001','20180214001','product001','SK-II神仙水',1,1590.0,'2018-02-14','SK-II'),
('offline002','counterB','customer002','20190315002','product002','雅诗兰黛小棕瓶精华液',3,899.0,'2019-03-15','雅诗兰黛'),
('offline003','counterC','customer003','20190416003','product003','兰蔻小黑瓶基底精华液',2,1080.0,'2019-04-16','LANCOME'),
('offline004','counterD','customer004','20190517004','product004','海蓝之谜修护精粹液',3,1150.0,'2019-05-17','LAMER'),
('offline005','counterE','customer005','20190618005','product005','玉兰油淡斑小白瓶精华',10,498.0,'2019-06-18','OLAY'),
('offline001','counterA','customer001','20180501012','product001','SK-II神仙水',2,1590.0,'2018-05-01','SK-II'),
('offline004','counterD','customer001','20180618065','product004','海蓝之谜修护精粹液',2,1150.0,'2018-06-18','LAMER'),
('offline004','counterD','customer001','20181110065','product004','海蓝之谜修护精粹液',6,1150.0,'2018-11-10','LAMER'),
('offline004','counterD','customer001','20190213033','product004','海蓝之谜修护精粹液',8,1150.0,'2018-06-18','LAMER'),
('offline002','counterB','customer002','20190429088','product002','雅诗兰黛小棕瓶精华液',5,899.0,'2019-03-15','雅诗兰黛'),
('online001','NA','customer002','20190616125','product002','雅诗兰黛小棕瓶精华液',6,899.0,'2019-06-16','雅诗兰黛'),
('offline003','counterC','customer003','20190617015','product003','兰蔻小黑瓶基底精华液',1,1080.0,'2019-06-17','LANCOME'),
('offline004','counterD','customer004','20190901024','product004','海蓝之谜修护精粹液',3,1150.0,'2019-09-01','LAMER'),
('offline005','counterE','customer005','20190818035','product005','玉兰油淡斑小白瓶精华',3,498.0,'2019-08-18','OLAY'),
('offline005','counterE','customer005','20191018077','product005','玉兰油淡斑小白瓶精华',5,498.0,'2019-10-18','OLAY'),
('offline002','counterB','customer005','20191110085','product005','雅诗兰黛小棕瓶精华液',2,899.0,'2019-11-10','雅诗兰黛');

INSERT INTO PROFILE VALUES
('customer001','2018-02-14',22),
('customer002','2019-03-15',25),
('customer003','2019-04-16',28),
('customer004','2019-05-17',33),
('customer005','2019-06-18',26);

学习笔记:SQL大厂面试题集_第6张图片学习笔记:SQL大厂面试题集_第7张图片 

1.计算每个客户在2019年的总花费(sales),购买的订单数frequency,每单平均花费AUS及
每单平均购买的数量PT

SELECT customer_id,SUM(quantity*unit_price)AS sales,COUNT(order_id)AS frequency,SUM(quantity*unit_price)/COUNT(order_id) AS aus,SUM(quantity)/COUNT(order_id)AS pt
FROM trans 
WHERE YEAR(purchase_date)=2019 
GROUP BY customer_id;

2.计算每一类客户在2019年每月产生的销售额(即购买金额>0),及发生购买的人数(客户类型分为新客、老客;新客:2019年产生第一次购买,老客:2019年之前已经购买过)

1.增加老客新标签
SELECT a.*,b.*,CASE WHEN YEAR(b.first_pur_date)=2019 THEN 'new' ELSE 'old' END AS customer_type
FROM trans a LEFT JOIN PROFILE b ON a.customer_id=b.customer_id
WHERE YEAR(a.purchase_date)=2019;
2.分类统计即可
SELECT DATE_FORMAT(purchase_date,'%Y-%m')AS date_ym,customer_type,SUM(quantity*unit_price)AS sale_money,COUNT(*)AS shop_people
FROM(SELECT a.*,CASE WHEN YEAR(b.first_pur_date)=2019 THEN 'NEW' ELSE 'OLD' END AS customer_type FROM trans a LEFT JOIN PROFILE bON a.customer_id=b.customer_id WHERE YEAR(a.purchase_date)=2019)t
GROUP BY MONTH(purchase_date),customer_type;

学习笔记:SQL大厂面试题集_第8张图片

3.现要给2019年新客发送A,B小样,线上新客发放A小样,线下新客发放B小样,请编写程序抽取相应的名单。字段COUNTERID:offine线下,online线上

SELECT DISTINCT t.customer_id,CASE WHEN t.counter_id LIKE 'online%' THEN 'A' ELSE 'B'END '小样类型'
FROM(SELECT a.*,CASE WHEN YEAR(b.first_pur_date)=2019 THEN 'NEW' ELSE 'OLD' END AS customer_type FROM trans a LEFT JOIN PROFILE b ON a.customer_id=b.customer_id WHERE YEAR(a.purchase_date)=2019)t
WHERE t.customer_type='new';

学习笔记:SQL大厂面试题集_第9张图片

4.展示每种品牌的销售数量,要求排序,并排出每家商铺对应占该品牌的销售额占比情况

-- 每种品牌的销售数量 销售额
SELECT brand,SUM(quantity)AS sum_quantity,SUM(quantity*unit_price)AS sales
FROM trans 
GROUP BY brand
-- 每家店铺的销售数量 销售额
SELECT brand,counter_name,SUM(quantity)AS sum_quantity,SUM(quantity*unit_price)AS sales
FROM trans 
GROUP BY brand,counter_name
-- 结合以上,计算占比
SELECT a.brand,a.counter_name,a.sales/b.sales AS'销售额占比',b.sum_quantity AS'销售数量'
FROM(SELECT brand,counter_name,SUM(quantity)AS sum_quantity,SUM(quantity*unit_price)AS sales FROM trans GROUP BY brand,counter_name)a 
LEFT JOIN(SELECT brand,SUM(quantity)AS sum_quantity,SUM(quantity*unit_price)AS sales FROM trans GROUP BY brand)b ON a.brand=b.brand
ORDER BY 销售数量 DESC;

学习笔记:SQL大厂面试题集_第10张图片

                                                                                                                                 

你可能感兴趣的:(sql,面试)