实战题精选

SQL 笔试与手撕题教程

大厂面试手撕 SQL 的真正难点

大厂数据分析岗手撕 SQL 的难点不在语法,而在业务理解。面试官给你的题目通常包含业务背景,你需要:

  • 理解指标口径——"活跃用户"是日活还是月活?"转化率"的分母是曝光还是点击?
  • 明确时间窗口——"近 7 天"包不包含当天?"月度"是自然月还是滚动 30 天?
  • 处理边界情况——新用户没有历史数据、某天销售额为 0、存在重复记录等

面试前一定要先熟悉目标公司的核心业务指标,下面是常见业务指标参照表。

常见业务指标参照表

面试前对照这张表,搞清楚你要面的公司最关心哪些指标:

业务场景常见指标口径要点
电商(拼多多、美团) GMV、客单价、复购率、转化率、退货率 GMV = 下单金额 vs 支付金额 vs 确认收货金额,口径差异大
广告(字节跳动) CTR、CVR、CPC、CPM、ROI、ARPU CTR = 点击/曝光,注意去重(同一用户多次曝光算几次?)
内容/社交 DAU/MAU、留存率、人均使用时长、互动率 DAU 通常按设备去重;留存率的基准日是注册日还是首次活跃日
金融/支付 交易额、支付成功率、坏账率、逾期率 注意区分发起交易 vs 完成交易,以及不同支付渠道的统计口径
通用 新增用户、活跃用户、流失用户、回流用户 "流失"定义因公司而异:连续 7 天/14 天/30 天未登录
实操建议
拿到题目后,先花 30 秒跟面试官确认:1)指标的定义和口径;2)时间范围;3)是否需要去重。这不是浪费时间,而是展示你的业务敏感度。

连续登录检测

经典的 Gaps and Islands 问题。核心技巧:连续日期减去连续序号 = 常量

比如一个用户在 4/1、4/2、4/3 登录,用 ROW_NUMBER 编号为 1、2、3。拿日期减序号:

三个日期减出来的值相同,说明它们是连续的。按这个值分组,COUNT(*) >= N 就能找出连续 N 天登录的用户。

练习

连续登录至少 3 天

高频字节美团给定 logins 表,找出连续登录至少 3 天的用户,输出 user_id、连续登录天数、起止日期。
SQL 编辑器

点击「运行」查看查询结果

查看答案
参考答案
WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY login_date) AS rn
  FROM logins
),
grouped AS (
  SELECT user_id, login_date,
    DATE_SUB(login_date, INTERVAL rn DAY) AS grp_key
  FROM numbered
)
SELECT user_id,
  COUNT(*) AS consecutive_days,
  MIN(login_date) AS start_date,
  MAX(login_date) AS end_date
FROM grouped
GROUP BY user_id, grp_key
HAVING COUNT(*) >= 3;
解析

DATE_SUB(login_date, INTERVAL rn DAY) 用日期减去行号,连续日期会得到相同的值,按此分组即可找出连续段。用户 1 有 4/1-4/3 连续 3 天,用户 2 有 4/3-4/6 连续 4 天。

每组 Top-N 与首事件归因

两个常见的窗口函数套路放在一起讲:

练习

首次下单前的最后访问来源

高频字节给定 page_visitsorders 两张表,找出每个用户首次下单前的最后一次页面访问来源。
SQL 编辑器

点击「运行」查看查询结果

查看答案
参考答案
WITH first_ord AS (
  SELECT user_id, MIN(order_time) AS first_order
  FROM orders GROUP BY user_id
),
visits_ranked AS (
  SELECT v.user_id, v.source, v.visit_time,
    ROW_NUMBER() OVER(PARTITION BY v.user_id ORDER BY v.visit_time DESC) AS rnk
  FROM page_visits v
  JOIN first_ord f ON v.user_id = f.user_id AND v.visit_time < f.first_order
)
SELECT user_id, source
FROM visits_ranked
WHERE rnk = 1;
解析

先找每个用户的首单时间,再筛出首单之前的所有访问,按时间倒序取第一条。用户 1 首单前最后访问来源是 wechat(4/4),用户 2 是 google(4/7)。

NULL 处理与增长计算

练习

订单增长额

给定 sales 表,计算每个用户每笔订单相比上一笔的增长额。第一笔订单的增长额显示为 0。
SQL 编辑器

点击「运行」查看查询结果

查看答案
参考答案
SELECT user_id, sale_date, amount,
  amount - COALESCE(LAG(amount) OVER(PARTITION BY user_id ORDER BY sale_date), amount) AS increase
FROM sales;
解析

LAG 第一行返回 NULL,COALESCE(..., amount) 让第一笔订单的前值等于自己,差值为 0。也可以用 LAG(amount, 1, amount) 的第三个参数达到同样效果。