飞道的博客

2019年秋招部分大厂SQL题

501人阅读  评论(0)

突发奇想,翻出去年秋招时候的几道笔试题做一做,看看我这刀是不是老了。总的来说我遇到的题目难度都还OK,不是很难,耐心、细心一点是没有什么问题的。

不嫌弃的话就一起来练练手吧。

01

某滴有一张订单表ordr_list,共有三列:ordr_id(订单id)-- bigint,uid(乘客id)-- bigint,start_time(发单时间)-- datetime,对于同一个乘客,每个订单的发单时间均不同。

解题要求:

1)写SQL求得每个用户最早发单的订单id,每个乘客一行;

2)输出两列:uid(乘客id),ordr_id(最早发单的订单id);

3)可使用union、left join、case when等,不能使用右连接、全连接、row_number()、rank()


   
  1. select a.uid, a.ordr_id
  2. from ordr_list as a
  3. left join 
  4. (   select uid, min(start_time) as min_time
  5.     from ordr_list
  6.     group by uid
  7. ) as b 
  8. on a.uid = b.uid and a.start_time = b.min_time;

注意:

1.做题时遵守题目要求;

2.注意题中给的数据库系统的类型,此题给的是SQLite3,有些函数不能用或不一致;

3.此题明明写了同一乘客,每个订单的发单时间均不同,但笔试的时候直接按上述代码做,并未运行通过,需要如下解法:


   
  1. select uid, min(ordr_id)
  2. from
  3. (   select a.uid, a.ordr_id
  4.     from ordr_list  as a
  5.     left  join 
  6. ( select uid, min(start_time) as min_time
  7. from ordr_list
  8. group by uid
  9. ) as b
  10. on a.uid = b.uid and a.start_time =b.min_time
  11. )t
  12. group by uid;

02

某巴巴有一张订单表table1,表中包含user_id(用户id),dt(购买日期),amt(购买金额),找出购买天数和购买金额最多的用户,按要求输出用户id、购买天数、购买金额、备注是购买天数最多还是购买金额最多。

解法1:


   
  1. with tmp as (
  2. select user_id, count( distinct dt) as sum_dt, sum(amt) as sum_amt
  3. from table1
  4. group by user_id
  5. )
  6. select user_id, sum_dt, sum_amt, '购买天数最多' as info
  7. from tmp
  8. where sum_dt = ( select max(sum_dt) from tmp)
  9. union all
  10. select user_id, sum_dt, sum_amt, '购买金额最多' as info
  11. from tmp
  12. where sum_amt = ( select max(sum_amt) from tmp);

解法2:


   
  1. select TOP  1 user_id,  count( distinct dt),  sum(amt),  '购买天数最多'  as info
  2. from table1
  3. group by user_id
  4. order by count( distinct dt) desc
  5. union all
  6. select TOP  1 user_id,  count(),  sum(amt),  '购买金额最多'  as info
  7. from table1
  8. group by user_id
  9. order by sum(amt) desc;

但需要注意的是,并非所有的数据库系统都支持此解法中的TOP。

03

某浪有一用户观看视频的行为记录表user_video,一次观看即一条记录,表结构大致如下,其中date,uid,vid为主键:

date(日期)
uid(用户id)
vid(视频id)
tag(视频标签)
20180711
1001
2001
体育
20180711
1002 2002 娱乐
...
...
...
...

每天新增用户的信息存于新增用户表中,表名为new_user_info,表结构大致如下,其中uid为主键:

uid(用户id)
new_date(日期)
new_chl(渠道)
new_chl_type(渠道类型)
1013
2018-06-30
11010
应用宝
1015
2018-07-01 12010 应用商店
...
...
...
...

用户信息存于用户信息表中,表名为user_info,表结构大致如下,其中uid为主键:

uid(用户id) gender(性别)
age(年龄)
city(城市)
1001

20
北京
1002 25 上海
...
...
...
...

1) 抽取各渠道类型的新增用户在7月10日,7月11日的人均观看次数,统计7月11日人均观看次数对比7月10日的涨幅,输出结果:第一列为渠道类型,第二列为人均观看次数,第三列为涨幅(保留4位小数,四舍五入)。


   
  1. select new_chl_type
  2. , round( sum(vid)/ count( distinct uid), 4)
  3. , round(( sum( case when date = '20200711' then vid else 0 end)/ count( distinct case when date = '20200711' then uid else null end))
  4. /( sum( case when date = '20200710' then vid else 0 end)/ count( distinct case when date = '20200710' then uid else null end)- 1, 4)
  5. from
  6. (
  7. select a.uid, a.new_chl_type, b.date, b.vid
  8. from new_user_info as a
  9. left join
  10. (
  11. select date, uid, count(vid) as vid
  12. from user_video
  13. where date <= '20190711' and date >= '20190710'
  14. group by date, uid
  15. ) as b
  16. on a.uid = b.uid
  17. ) as t
  18. group by new_chl_type;

2) 取25岁以下女性用户在美食标签2019年8月1日-7日观看视频次数超过10次且8月8日观看次数小于3次的用户,输出结果:输出符合条件的用户id。


   
  1. select a.uid
  2. from
  3. (
  4. select uid
  5. from user_info
  6. where age < 25 and gender = '女'
  7. group by uid
  8. ) as a
  9. left join
  10. (
  11. select uid, count(vid)
  12. from user_video
  13. where tag = '美食' and date <= '20190807' and date >= '20190801'
  14. group by uid
  15. having count(vid) > 10
  16. ) as b
  17. on a.uid = b.uid
  18. left join
  19. (
  20. select uid
  21. from user_video
  22. where tag = '美食' and date = '20190808'
  23. group by uid
  24. having count(uid) < 3
  25. ) as c
  26. on a.uid = c.uid
  27. where b.uid is not null and c.uid is not null;

注意:

多表连接可能导致效率变低,此处也可以考虑使用case when进行条件判断,然后再筛选。

04

某多有一张用户订单表,表名为ordr_tbl,字段为user_id(用户id),ordr_id(订单id),ordr_goods(订单商品),ordr_time(下单时间)。还有一张用户商品点击明细表,表名为clk_tbl,字段为clk_id(点击id),user_id(用户id),clk_time(点击时间),clk_goods(点击商品名)。假设只有一天数据,点击和下单必须是同一用户同一商品,多次点击然后下单的话算最后一次点击。输出用户点击后下单的记录:clk_id,ordr_id。


   
  1. select a.clk_id, b.ordr_id
  2. from
  3. (
  4. select *
  5. from
  6. (
  7. select *, row_number() over( partition by user_id, clk_goods order by clk_time desc) as rnk
  8. from clk_tbl
  9. ) as t
  10. where rnk = 1
  11. ) as a
  12. left join ordr_tbl as b
  13. on a.user_id = b.user_id
  14. and a.clk_goods = b.ordr_goods
  15. and a.clk_time < b.ordr_time;

注意:

不要丢失重要的限定条件,比如对于成交来说,点击时间肯定是早于下单时间的。

05

附赠一道求留存的题目,模糊记忆,是提前批的时候英语流利说考到的,那时候那道题可是难倒了周围不少小伙伴。不是题目本身难,而是因为不熟悉留存的定义,无法向下进行,而这道题目对于实习过的同学来说可能就比较友好了。

有一张活跃用户表active_user,包括两个字段:pt('年-月-日'),user_id(用户id),求当天用户在第二天、第三天的回访比例。


   
  1. select a.pt
  2. , count( distinct case when a.pt = date_sub(b.pt, 1) then a.user_id else null end)/ count( distinct a.user_id) as '第二天回访比例'
  3. , count( distinct case when a.pt = date_sub(b.pt, 2) then a.user_id else null end)/ count( distinct a.user_id) as '第三天回访比例'
  4. from active_user as a
  5. left join active_user as b
  6. on a.user_id = b.user_id
  7. group by a.pt;

注意:

第二天的回访比例也就是常说的次留(次日留存率),上述方法就是以user_id为连接键,通过判断当天活跃的用户在第二天(第n天)还在不在来计数求取。除求留存外,求新增用户也常出现在笔面试中。

最后对先前推文中第一题的答案给出纠正。

原文链接在这:解一下TMD几道热门数据分析面试题。


   
  1. select user_name, goods_kind
  2. from
  3. (
  4. select user_name, goods_kind, num, row_number() over( partition by user_name order by num desc) as rnk
  5. from
  6. (
  7. select user_name, goods_kind, count( 1) as num
  8. from user_goods_table
  9. group by user_name, goods_kind
  10. ) as t1
  11. ) as t2
  12. where rnk = 1;

其实原答案思路与这个是一致的,只是有些函数及组合不能并列使用,或者说并列使用并不符合逻辑。在使用SQL做题的时候,如果无法从原表中直接得到想要的数据,就需要先进行一次select,形成一个子查询。而此子查询能否成立需要在脑中形成一个表,这个表的各个列及行数据是否能准确形成,是否符合逻辑,以此进行判断。原答案中goods_kind如何与rank进行对应就是个问题了,可以考虑看看。

写在最后:

1.对于SQL题目,其实正经来说,比算法coding要简单的多。最基本的要求就是知道基础的函数及使用方法,就像做数学题一样,遇到什么类型的题目,需要用到什么样的解法,做的多了,自然也就熟悉了,也就会更熟练。

2.做题时注意题目中所给的条件,一些条件是显性的,如“超过”、“小于”、“最早”、“最多”、“几分之几”、“前百分之多少”等等;一些条件是隐性的,需要自己细心才能注意到,如上题中“点击时间小于下单时间”,还有一些题目中“快递邮寄时间早于快递接收时间”等。

3.有自己良好的书写习惯和风格,是先写大框架再填补,还是从内而外书写SQL语句,是通过空格或tab进行缩进来标志不同的子查询还是直接通过括号就能识别出来。好的习惯不仅给别人的可读性好,给自己在遇到逻辑毕竟复杂的题目时,也留有了后路。

4.做完题目记得复查。子查询是否定义了别名、表连接时select出两表中同名字段时需要加表别名、各个条件是否已与题目中条件一一对应等等,犯错的次数多了,记忆会更加深刻,也会更加注意,要学会总结。

5.最后再强调一次,想刷SQL题目,除了牛客和leetcode,请从牛客上找笔面经中的SQL题目练习,从各种公众号推文中练习。以后再有人问我从哪里练习SQL题,此种问题一概不回答。

6.上述题目有错误的地方或更好的方法欢迎指正。

扫码关注我吧


转载:https://blog.csdn.net/lee__leee/article/details/107603119
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场