SQL面试题总结

一、面试常见题型

参考:【数分面试宝典】大厂高频SQL笔试题(二) (qq.com)

1. 平均问题

SQL32 牛客直播各科目平均观看时长

牛客直播各科目平均观看时长_牛客题霸_牛客网 (nowcoder.com)

select course_name, round(avg(TIMESTAMPDIFF(MINUTE, in_datetime, out_datetime)), 2) as avg_Len
from course_tb as t1
right join attend_tb as t2
on t1.course_id = t2.course_id
group by course_name
order by avg_Len desc

去除最高最低的平均

# 使用窗口函数RANK分别对工资salary进行升序和降序排列,就获得了该分组内最低和最高的工资,
# 过滤掉这2条记录再对工资salary进行平均avg即可
select a.department,avg(a.salary)
from 
 (
 select *,
 rank() over( partition by department 
 order by salary ) rank_asc,
 rank() over( partition by department 
 order by salary desc) rank_desc
 from emp
 ) a
group by a.department
where a.rank_asc >1 and a.rank_desc >1

2. 新用户与活跃用户问题

【关键点掌握】

1. 新用户的判定——依据最小登入时间为准;

2. 活跃用户的判定——登入和登出时间都是活跃时间,需要union all 连接

SQL11 每天的日活数及新用户占比

每天的日活数及新用户占比_牛客题霸_牛客网 (nowcoder.com)

select a.dt,count(distinct a.uid) dau,round(count(distinct b.uid)/count(distinct a.uid),2) # 4. a表是所有活跃记录,b表是新用户活跃记录
from 
        (select uid,date(in_time) dt from tb_user_log
        union all # 1. 登入和登出时间连接(两个时间都可以证明其在对应当天活跃)
        select uid,date(out_time) dt from tb_user_log
        group by uid,dt # 1.1 分组将用户和时间分组
         ) a
left join # 2. 左连接
         (select uid,min(date(in_time)) dt from tb_user_log group by uid # 3. 求出每个用户的最小时间,即成为新用户的时间
         ) b
on a.uid=b.uid and a.dt=b.dt # 4. 将用户和时间关联
group by a.dt
order by a.dt;

3. TOP N 问题

SQL37 某乎问答单日回答问题数大于等于3个的所有用户

某乎问答单日回答问题数大于等于3个的所有用户_牛客题霸_牛客网 (nowcoder.com)

select answer_date,
       author_id,
       count(issue_id) answer_cnt
from answer_tb
where month(answer_date) = 11
group by answer_date,author_id
having count(issue_id)>=3
order by answer_date,author_id

select 
product_id, user_id, cnt
from
(
  select 
  product_id, user_id, cnt,
  rank() over(partition by product_id order by cnt) rn # 2. 每个商品被浏览次数排名
  from 
  (
    select # 1. 计算每个商品被每个用户浏览的次数
    product_id, user_id, count(*) cnt
    from product_view
    group by product_id, user_id
  )t1
)t2
where rn<=3; # 3. 计算每个商品浏览前3的用户

4. 连续N天问题

SQL29 某宝店铺连续2天及以上购物的用户及其对应的天数

https://www.nowcoder.com/practice/63ac3be0e4b44cce8dd2619d2236c3bf?tpId=268&tqId=2285906&ru=/exam/oj&qru=/ta/sql-factory-interview/question-ranking&sourceUrl=%2Fexam%2Foj%3Ftab%3DSQL%25E7%25AF%2587%26topicId%3D268

SELECT
    user_id, COUNT(*) AS days_count
# 1. 完成内层子查询,DENSE_RANK()排序
FROM ( SELECT
            DISTINCT user_id, sales_date, DENSE_RANK() OVER (PARTITION BY user_id ORDER BY sales_date) AS rk
       FROM 
            sales_tb) as t1
# 2. 完成GROUP BY user_id, DATE_SUB(sales_date, INTERVAL rk DAY)
GROUP BY
    user_id, DATE_SUB(sales_date, INTERVAL rk DAY)
# 3. 完成HAVING days_count >= 2
HAVING
    days_count >= 2
ORDER BY
    user_id;

5. 累计问题

SQL4 每个创作者每月的涨粉率及截止当前的总粉丝量

每个创作者每月的涨粉率及截止当前的总粉丝量_牛客题霸_牛客网 (nowcoder.com)

SELECT
    author, month, fans_growth_rate,
# 2. 利用开窗函数 SUM(fans_month),完成累计总量计算
    SUM(fans_month) OVER (PARTITION BY author ORDER BY month) AS total_fans
FROM
# 1. 完成内层子查询,【每个用户】(或视频,商品)【每个月】的【粉丝量】(或观看量,销售量)
(SELECT
    author, DATE_FORMAT(start_time, '%Y-%m') AS month,
    SUM(IF(if_follow = 2, -1, if_follow)) AS fans_month, #1.1 求和得到用户当月总量
    ROUND(SUM(IF(if_follow = 2, -1, if_follow)) / COUNT(*), 3) AS fans_growth_rate 
FROM
    tb_video_info AS t1
LEFT JOIN
    tb_user_video_log AS t2
ON
    t1.video_id = t2.video_id
WHERE
    YEAR(start_time) = 2021
GROUP BY
    author, month) AS t #1.2 按照【每个用户】(或视频,商品)【每个月】
ORDER BY
    author, total_fans;

6. 最大同时在线问题

  1. UNION ALL 连接在线表和下线表
  2. sum(diff)over(partition by XX order by XX,XX DESC) 开窗函数求和
  3. max(XX)求最大值

SQL8 每篇文章同一时刻最大在看人数

每篇文章同一时刻最大在看人数_牛客题霸_牛客网 (nowcoder.com)

SELECT artical_id,max(instant_view) max_uv # 3. max()求最大值
FROM(SELECT artical_id,sum(diff)over(partition by artical_id order by dt,diff DESC) instant_view # 2. 窗函数求和
    FROM(SELECT artical_id,in_time dt,1 diff FROM tb_user_log WHERE artical_id!=0 # [关键] 1 diff
        UNION ALL # 1. UNION ALL 连接在线表和下线表
        SELECT artical_id,out_time dt,-1 diff FROM tb_user_log WHERE artical_id!=0 # [关键] -1 diff
        ) a
     )b
GROUP BY artical_id
ORDER BY max_uv DESC;

SQL34 牛客直播各科目同时在线人数

牛客直播各科目同时在线人数_牛客题霸_牛客网 (nowcoder.com)

SELECT T2.course_id, T2.course_name, MAX(T1.num) max_num # 3. max()求最大值
FROM (
    SELECT course_id, SUM(tag) OVER(PARTITION BY course_id ORDER BY time, tag DESC) num # 2. 窗函数求和
    FROM (
        SELECT user_id, course_id, in_datetime time, 1 tag FROM attend_tb # [关键] 1 diff
        UNION # 1. UNION ALL 连接在线表和下线表
        SELECT user_id, course_id, out_datetime time, -1 tag FROM attend_tb # [关键] -1 diff
    ) TA
) T1
INNER JOIN course_tb T2 ON T2.course_id = T1.course_id
GROUP BY T2.course_id, T2.course_name
ORDER BY T2.course_id;

7. 次日或7日留存率问题

SQL29 计算用户的平均次日留存率

计算用户的平均次日留存率_牛客题霸_牛客网 (nowcoder.com)

SELECT
    COUNT(q2.device_id) / COUNT(q1.device_id) AS avg_ret
FROM
    (SELECT DISTINCT device_id, date FROM question_practice_detail)as q1
LEFT JOIN
    (SELECT DISTINCT device_id, date FROM question_practice_detail) AS q2
ON 
    q1.device_id = q2.device_id AND q2.date = DATE_ADD(q1.date, interval 1 day)

【数分面试宝典】大厂高频SQL笔试题(三) (qq.com)

select d.a_day,
count(distinct d.userid) as '活跃用户数',
count(distinct case when d.day_diff=1 then d.userid else null end) as '次日留存数', 
count(distinct case when day_diff=1 then d.userid else null end)/count(distinct d.userid) as '次日留存率',
count(distinct case when d.day_diff=3 then d.userid else null end)as '3日留存数', 
count(distinct case when day_diff=3 then d.userid else null end)/count(distinct d.userid) as '3日留存率',
count(distinct case when d.day_diff=7 then d.userid else null end) as '7日留存数' ,
count(distinct case when day_diff=7 then d.userid else null end)/count(distinct d.userid) as '7日留存率'
(SELECT 
*, date_sub(b_day,a_day) as day_diff
FROM
	(SELECT DISTINCT a.userid,
	a.active_day AS a_day,
	b.active_day AS b_day
	FROM active_log AS a  
	LEFT JOIN active_log AS b
	ON a.userid=b.userid) t) tt

二、面经题汇总

充值额最大的账号和金额

求平均成绩大于80,且0001课程分数高于0002课程分数的学生id

每个班每科成绩top10(怎么优化/不使用窗函数怎么做)

给一个班的数据成绩进行排名

公交车人数最高的时间段

用户登录表A,字段:user_id,device_id,login_date,求用户最大连续登录天数

有这样一个表,时间戳 用户id 用户行为,怎么计算出每天的新用户数量

实时计算每天一小时内各个种类的热门Top10商品

未解决的题

给定视频分享的from_user_id and to_user_id,找出视频分享的源头用户,以及视频分享的最大深度。



三、总结

一些解题思路:

  1. 将问题拆解,一步一步实现
  2. 考虑是使用join(需要补充一些数据时),还是where(需要排除一些数据时),还是子查询()

一些注意事项:

  1. 注意是否使用left/right join(外连接不匹配的会去null)
  2. 注意是否使用distinct
  3. where中不能有函数;
  4. count(null)=0;类似count后面和括号之间不能有空格
  5. or自带去重,而union等价于or,但union all 不去重
  6. date可以直接大小比较,date>'2025-10-15'
  7. if的用法sum(if(result = "right",1,0));ifnull(expr1,expr2),不为null则取expr1,为null则取expr2
  8. 使用类似max的分组函数,没办法保证同一行的数据被统一取出,除了max的列,其他列回默认取第一行的数据
posted @ 2022-03-29 10:48  HENRYWHALE  阅读(290)  评论(0)    收藏  举报