2026/10/21 8:17:24

电商数据分析SQL面试真题解析:从连续登录到留存率的解题思路

电商数据分析SQL面试真题解析:从连续登录到留存率的解题思路 回顾前几个月集中准备数据分析岗位面试的经历最有感触的其实是SQL这道坎。投过头部电商平台数据分析岗的都知道笔试和一面基本就是SQL题专场甚至到了二面三面面试官还会临时抛一道现场写SQL的场景题。我前前后后刷了不下两百道题也帮忙整理过模拟面试题集过程中踩了不少坑也总结出了一套应对这类面试题的方法。这篇就把我整理的电商数据分析SQL面试高频真题、解题思路和容易翻车的地方一次性说透。如果你正在准备数据岗面试或者想检验自己的SQL水平是否达到电商数据分析的实操要求这篇文章可以帮你省下不少盲目刷题的时间。整体会围绕“题目到底在考什么”和“怎么写才能拿分”两条线展开每道题都配了可运行的标准SQL实现和面试官追问的回答思路。1. 电商数据分析SQL面试到底在筛什么1.1 表面考语法实际考业务拆解能力很多候选人以为SQL面试是考记忆、考函数、考语法其实面试官真正想看的是你拿到一个模糊的业务问题时能不能把它拆成清晰的逻辑步骤。就拿“计算复购率”这个需求来说业务口径就有好几种定义是按自然月计算还是按用户首次下单后N天内计算复购是按订单数算还是按下单天数算不同口径得出的数字可能差好几个点面试官要看到的是你对业务口径的判断和主动确认的动作而不只是会写一个count。我见过不少候选人窗口函数、连表、子查询都用得很熟但一遇到“哪些用户在连续N天内有下单”这种实际问题就开始手忙脚乱。根本原因就是平时刷题都在做纯语法练习没有养成“先拆业务、再写SQL”的习惯。电商数据分析岗位对SQL的要求不只是会取数还要能通过SQL理解业务变化的原因这才是面试环节设置SQL题的真实意图。1.2 从岗位职责反推考点权重从多家电商公司的数据分析岗JD来看SQL相关的职责描述通常包括日常数据提取与分析、业务监控与异常诊断、专题分析与ABTest支撑、数据报表开发。对应到面试题考点分布大概是这样能力模块典型面试题类型出现频率多表关联查询订单表与用户表、商品表关联取数很高聚合与分组按时间/品类/用户维度统计GMV、订单量很高窗口函数排名、TopN、同比环比、累计计算很高时间函数连续登录、留存率、月份计算高业务指标计算复购率、客单价、付费率、漏斗转化高复杂逻辑去重、最大连续天数、中位数、异常值中性能优化数据倾斜、索引、子查询优化低但常作为加分项这里有一个容易被忽略的点面试官不会只关心你写出来的结果对不对更会关注你写的SQL是不是够简洁、够高效。比如能用一次窗口函数解决的问题就不要嵌套三层子查询能用一个group by完成的统计就不要先查明细再在外面包一层。这些都是面试现场拿印象分的关键。1.3 常见的SQL面试流程与考察节奏电商数据分析的面试通常分两轮技术考察。第一轮是笔试或线上测评一般给4到6道SQL题限时60到90分钟覆盖取数、聚合、窗口函数、业务指标计算这几个核心模块。第二轮是业务面面试官会结合具体业务场景现场提问比如“给你一张订单表和一张退款表怎么分析退款率异常的原因”然后要求你边讲思路边写SQL。我在模拟面试中发现一个规律大部分候选人笔试能写完但一到现场口头讲题就卡壳。这其实是可以刻意练习的。你需要在写每一步时都能说清楚“我为什么要做这一步转换”“这个指标的业务含义是什么”“如果数据量很大这个写法会不会有问题”。而这种表达能力只能通过大量做真题、复盘思路来训练。2. 高频真题拆解连表、聚合与业务口径2.1 题目1连续N天下单的用户这是各大电商平台最爱出的题目之一考察的是窗口函数加时间函数的组合应用。原题一般是给一张下单记录表包含用户ID和下单日期要求找出连续3天及以上有下单记录的用户。考察点对窗口函数row_number的理解、日期差的计算、分组聚合的粒度切换。标准解法是这样的select user_id from ( select user_id, order_date, date_sub(order_date, row_number() over (partition by user_id order by order_date)) as date_group from ( select distinct user_id, order_date from order_table ) t1 ) t2 group by user_id, date_group having count(1) 3;这个解法的核心逻辑是“日期减去序号是否变化”。如果用户1号、2号、3号连续下单row_number分别生成1、2、3减去之后得到的都是同一天说明这是连续区间。如果中间断了一天减出来的日期就会发生变化。最后的having count来判断连续天数是否达标。这道题有两点特别容易踩坑。第一去重过程不能少因为用户一天可能有多笔订单如果不先去重会产生错误的连续区间。第二date_sub函数在不同数据库里的写法略有差异比如某些场景下也可以写成 order_date - row_number()但在Hive里建议使用date_sub明确转换。面试官追问如果要求的是“连续下单天数最多的前10名用户”怎么写这实际上是把连续区间的逻辑封装成子查询再计算每个用户最大的连续天数然后排序取前10select user_id, max(continuous_days) as max_days from ( select user_id, date_group, count(1) as continuous_days from (...) group by user_id, date_group ) a group by user_id order by max_days desc limit 10;我在讲解时通常会补一句如果一张表里有多家店铺的数据记得先过滤店铺维度否则用户在不同店铺下单也会被算成连续这个和业务上的“全站连续下单”定义是矛盾的。这种细节多说一句面试官对你的印象分会明显上涨。2.2 题目2每个品类下销售额Top3的商品这道题考察窗口函数中的排名函数以及“先聚合、后排名”的逻辑顺序。原题给一张订单明细表、一张商品信息表要求统计每个品类下销售额排名前3的商品。考察点join关联、group by聚合、rank或dense_rank的选用、TopN的取法。先做聚合再做排名是核心思路select category_id, product_id, sales_amount, sales_rank from ( select b.category_id, b.product_id, sum(a.order_amount) as sales_amount, row_number() over (partition by b.category_id order by sum(a.order_amount) desc) as sales_rank from fact_order a left join dim_product b on a.product_id b.product_id group by b.category_id, b.product_id ) t where sales_rank 3;这里有一个性能上的注意点如果数据量很大join之后再分组会比先分组再join更慢。更优的做法是先在订单表里按product_id聚合出销售额然后再join商品表获取品类信息最后再开窗排名。我在实际面试中会这样优化select b.category_id, a.product_id, a.sales_amount from ( select product_id, sum(order_amount) as sales_amount from fact_order group by product_id ) a left join dim_product b on a.product_id b.product_id ;然后在内存表基础上做窗口排名。这个顺序调整面试官问到你“数据量大了怎么办”时可以直接抛出来很容易加分。排名函数的选择也是高频追问点row_number、rank、dense_rank分别适用于什么场景。简单说row_number生成唯一连续序号rank并列会跳号dense_rank并列不跳号。业务上如果明确要取Top3且允许并列可以用dense_rank如果要求“不管是否并列只取3条”用row_number或者rank加limit都可以。部分候选人在这道题上会把聚合和窗口函数写在一个层次上导致语法报错这块也需要提前练习到肌肉记忆的程度。2.3 题目3GMV月度同比环比计算电商面试题里指标计算出现的频率极高其中GMV的同比环比又是必考中的必考。原题一般给订单表包含订单日期和订单金额要求按月统计GMV并计算每个月的环比增长率和同比增长率。考察点日期格式化、窗口函数lag的用法、同比环比的业务口径。select month_id, gmv, lag(gmv, 1) over (order by month_id) as prev_month_gmv, round((gmv - lag(gmv, 1) over (order by month_id)) / lag(gmv, 1) over (order by month_id), 4) as mom_rate from ( select date_format(order_date, %Y-%m) as month_id, sum(order_amount) as gmv from fact_order where order_status completed group by date_format(order_date, %Y-%m) ) t;同比的逻辑一样只不过lag的偏移量改成12同时需要保证数据跨度至少覆盖13个月lag(gmv, 12) over (order by month_id) as prev_year_gmv这道题有多个细节可以聊。第一如果订单表里还包含退款订单是否需要剔除通常GMV定义是拍下订单金额而GTV或实际成交额需要剔除退款这就要看业务口径了。面试时主动确认口径比闷头写完整道题要安全得多。第二lag的默认值问题如果没有前一期数据返回NULL这就需要在计算时用case when处理。第三环比计算除数为0时的处理线上数据里偶尔会有某个月份GMV为0的情况直接用除法会导致返回NULL或报错稳妥做法是配合nullif处理。我在模拟面试时一定会追问“如果我要按周统计日期函数应该怎么改”答案是把date_format的粒度从‘%Y-%m’改成‘%Y-%W’或通过date_sub计算到周一这既能考察候选人对日期函数的熟练度也能看出他有没有真正理解聚合粒度这个概念。2.4 题目4用户累计消费金额与消费等级这类题考察累计计算一般给订单表要求计算每个用户截至每个订单日期的累计消费金额并据此划分消费等级。考察点窗口函数sum与order by的组合、累计计算与窗口边界控制。select user_id, order_date, order_amount, sum(order_amount) over (partition by user_id order by order_date) as cumulative_amount from fact_order ;接下来基于累计金额划分等级比如0到1000为普通用户1000到5000为银牌用户5000以上为金牌用户select user_id, cumulative_amount, case when cumulative_amount 1000 then 普通用户 when cumulative_amount 5000 then 银牌用户 else 金牌用户 end as user_level from ( select user_id, max(cumulative_amount) as cumulative_amount from ( select user_id, sum(order_amount) over (partition by user_id order by order_date) as cumulative_amount from fact_order ) a group by user_id ) t;这里有几个值得展开讲的重难点。窗口函数sum() over(partition by ... order by ...) 默认的窗口范围是从分区的第一行到当前行这正是累计计算的原理。如果你在order by后面写了rows between unbounded preceding and current row含义更明确也是等价写法。第二个重点是“按用户取截至当前最大累计金额”。这里很多人会直接用“最新一笔订单的累计金额“来代表用户当前累计消费这在订单表存在跨天更新时会有问题。稳妥做法是先算累计序列再按用户取max或者取最后一条订单的累计值。但在面试中取max更稳妥因为不受明细表当天更新状态的影响。还有一点关于case when的顺序条件的先后会直接影响结果比如划分区间时要把大区间写在前面否则前面的条件会把后面区间的数据吞掉。这种低级错误其实很容易出现在面试现场因为紧张而忘记检查。我建议所有候选人最后都要自查一遍边界值比如1000元整到底属于哪个等级。3. 进阶业务场景留存、漏斗与归因分析3.1 题目5用户留存率计算留存率是电商和互联网数据分析面试中我认为最值得深究的题目之一。它考察的不只是SQL能力还有对留存R1、R3、R7等业务口径的理解。原题给用户注册表和登录或下单记录表要求计算某个月新增用户在注册后第1天、第3天、第7天的留存率。考察点日期差计算、条件聚合、多表关联的粒度控制。一种通用写法是先算每个用户的注册日期与活跃日期之差再判断是否等于1、3、7select b.reg_month, count(distinct a.user_id) as new_users, count(distinct if(datediff(a.active_date, b.reg_date) 1, a.user_id, null)) as retain_day1, count(distinct if(datediff(a.active_date, b.reg_date) 3, a.user_id, null)) as retain_day3, count(distinct if(datediff(a.active_date, b.reg_date) 7, a.user_id, null)) as retain_day7 from active_table a join ( select user_id, reg_date, date_format(reg_date, %Y-%m) as reg_month from user_reg_table where date_format(reg_date, %Y-%m) 2025-03 ) b on a.user_id b.user_id group by b.reg_month;这道题我特别喜欢用来考察候选人的细节敏感度。很多人在计算留存时直接用“注册日期1活跃日期”来判断这在小数据量时没问题但如果活跃日期有跨天偏移、或者活跃表记录的是精确到秒的时间戳就必须先做date的截断处理。另一个常见错误是用了inner join而不是left join导致没有后续活跃行为的注册用户直接被过滤掉留存率虚高。面试官追问通常会往两个方向走一是定义“活跃”的口径比如是有登录行为还是要有下单行为二是留存表的输出格式是要宽表每列是一个留存天数还是窄表每天一行留存记录。作为候选人你可以主动给出宽窄表两种写法并说明各自适合什么下游场景。这个主动性会拉开档次。3.2 题目6用户下单漏斗分析漏斗分析是电商业务的数据分析岗面试题里比较进阶的题目。原题一般给一张用户行为日志表包含用户ID、行为时间、行为类型曝光、点击、加购、下单、支付要求计算各个环节之间的转化率。考察点条件聚合、子查询、转化率口径定义。我常用的写法是先用条件聚合把每个用户的每个行为是否有转化统计出来再做漏斗select sum(if(is_expose 1, 1, 0)) as step1, sum(if(is_click 1, 1, 0)) as step2, sum(if(is_addcart 1, 1, 0)) as step3, sum(if(is_order 1, 1, 0)) as step4, sum(if(is_pay 1, 1, 0)) as step5 from ( select user_id, max(if(action expose, 1, 0)) as is_expose, max(if(action click, 1, 0)) as is_click, max(if(action addcart, 1, 0)) as is_addcart, max(if(action order, 1, 0)) as is_order, max(if(action pay, 1, 0)) as is_pay from user_behavior_log where dt 2025-03-15 group by user_id ) a;然后计算每两层之间的转化率。这里的核心注意点是漏斗分析里每一层的用户集合应该是上一层的子集。如果直接用多个count分别统计得到的其实是“总用户中有多少人发生了某行为”而不是真正的漏斗。更严谨的做法是把每一步的约束条件都限定在前置行为发生的用户之内。这个点很多候选人都会忽略一旦你主动指出来面试官通常都会认可。另外漏斗分析还有“时间窗口”这个维度比如用户从曝光到下单要在多久之内才算有效转化。如果要看一天内完成的转化需要给行为时间加时间差限制。这种扩展问题的价值在于展示你对业务分析场景的理解深度。3.3 题目7订单退款率异常归因分析这类题目更像业务面试里的综合题。面试官会给一张订单表和一张退款表并告诉你“最近一周退款率明显上升请分析可能的原因”要求用SQL配合分析思路作答。考察点从数据角度拆解业务问题的框架能力。需要写SQL的部分通常是计算不同维度下的退款率变化比如按商品品类、按支付渠道、按用户等级、按城市select b.category_name, count(distinct a.order_id) as order_cnt, count(distinct c.refund_order_id) as refund_cnt, count(distinct c.refund_order_id) / count(distinct a.order_id) as refund_rate from fact_order a left join ( select order_id from fact_refund where refund_status success and refund_date between 2025-03-08 and 2025-03-15 ) c on a.order_id c.order_id left join dim_product b on a.product_id b.product_id where a.order_date between 2025-03-08 and 2025-03-15 group by b.category_name order by refund_rate desc;分析思路上我会建议候选人按“内部因素”和“外部因素”两层框架来展开内部因素是商品或运营的变化比如某些品类的商品质量出问题、大促后的退款高峰期、某渠道的支付成功率下降外部因素包括竞品促销带来的用户比价行为、天气等环境因素的影响。如果你能结合SQL查询结果说“先按品类看哪些品类异常再看退款原因分布”面试官会认为你有完整的诊断思路而不仅仅是会写两条SQL。4. 面试现场容易翻车的五个SQL细节4.1 聚合粒度搞混这是排名第一的翻车原因。同一个指标由于关联表不同行数会翻倍。比如订单明细表join商品维表一般是一对一没有风险但订单表join退款表可能是一对多因为一个订单可能有多次部分退款记录导致金额sum重复计算。解决思路是先在子查询里把退款表按订单ID聚合好再与订单表关联这是我在写所有多表关联前都会先想一遍的关键逻辑。4.2 窗口函数使用边界很多候选人会把窗口函数直接写在where条件里或者在一个查询里同时使用聚合函数和普通字段而没有用group by。这些都属于SQL基础不扎实的表现。窗口函数的执行顺序要记住在group by和having之后在select阶段执行因此不能在where里过滤窗口函数的结果只能在外面包一层子查询。这条规则几乎每场面试都会碰到。4.3 NULL值处理路径不清计算转化率、占比时NULL值经常被忽略。比如left join过程中右表没有匹配记录相关字段就是NULL。如果直接拿NULL做分母或判断条件结果可能完全错误。建议养成三个习惯聚合前用coalesce或ifnull补默认值判断时用is null而不是 null除法运算中分母使用nullif避免返回null或报错。4.4 日期字段类型不统一订单日期有的是datetime有的是string有的是date不同表之间join时经常出现类型不一致问题。面试时不一定会跑真实数据但候选人最好主动说出“这里需要先把日期格式统一”这类话。常见的处理方式是date_format或cast也要注意Hive和MySQL在日期函数上的差异。4.5 只写答案不讲思路这是面试中最致命的一个问题。面试官让你现场写题不只是想看最终结果更想看你如何思考。候选人应该先用自己的话复述一遍题目理解再明确业务口径然后给出整体思路最后动手写SQL。写完后主动检查边界情况比如“如果这个用户没有下单记录怎么办”“如果同一商品有多条记录会不会重复计算”。这一套流程走下来比单纯写对一道题要加分得多。5. 备考路线与刷题建议5.1 三阶段备考规划第一阶段是基础语法巩固重点刷单表查询、聚合、排序、连接查询确保没有语法盲区。这一阶段可以用面试高频题直接练不需要重复做简单的练习题而是直接挑战真题难度。第二阶段是业务场景专项集中练窗口函数和复杂业务指标。连续登录、留存率、复购率、TopN、同比环比、漏斗转化每类至少做五道以上。做完一道题后马上复盘还有没有更优的写法换个业务口径能不能适用如果数据量翻100倍这个写法还能跑吗第三阶段是模拟面试与表达训练。建议找完全陌生的题目限时20分钟写完并口头讲解。讲解时可以按照“确认口径 → 拆解思路 → 写SQL → 检查边界→ 数据验证”这个顺序。反复练十道题以上能解决大部分面试紧张和表达混乱的问题。5.2 如何利用模拟数据建数据集我比较推荐自己造一套模拟电商数据来练因为这样能完整覆盖所有题型。建三张表就够了用户表user_id、reg_date、user_level、city、订单表order_id、user_id、product_id、order_date、order_amount、order_status、商品表product_id、product_name、category_id、category_name。再加一张用户行为日志表用来练漏斗分析和留存。模拟数据规模建议造10万用户、50万订单、1万商品既能在本地跑得动又能覆盖数据倾斜的极端场景。自己造数据的过程本身就是在练SQL因为你需要用SQL生成随机数据、插入表、做数据校验。5.3 面试准备中的两个加分习惯第一个加分习惯是“主动给口径”。任何一道指标计算题先说出“这里GMV的口径我按支付成功金额来算”或“退款订单我会单独标记”比闷头写SQL强得多。面试官会觉得你有真实业务经验。第二个加分习惯是“主动提性能优化”。写完SQL后加一句“如果这个表是每天千万级的增量我会先把过滤条件条件下推再做join和聚合”这个动作在面试里出现一次就足够让面试官记住你。我自己带过的模拟面试候选人中做到这一条的通过率明显高于平均水平。6. 个人经验分享与最后提醒写到这里再分享几个我在真实面试和带人过程中反复验证过的体会。SQL题完全没有必要追求“答案唯一”。同一个需求有十种写法面试官更看重的是思路和取舍。你不一定要把最优解写在第一版但你要能解释清楚你的第一版哪些地方还可以优化以及你为什么在当前业务量级下选了这个写法。另外一个很重要的心态是遇到完全没见过的题,先别慌。电商数据分析的SQL题再怎么变底层就是“多表关联、聚合统计、窗口函数、时间处理”这几板斧。把题目翻译成业务逻辑再翻译成SQL语法大部分题都能拆出来。我见过不少候选人在面试里写出正确答案后自己都不信“这个居然这样就解出来了。”实际上SQL面试题并没有想象中那么高不可攀。最后如果你现在还在为一道连续登录题卡住或者拿到留存率题目没有头绪可以先放下焦虑。把基础题刷熟每道题都做三遍——第一遍独立写第二遍对着答案改第三遍隔两天重新盲写。这个过程虽然笨但比任何取巧的方法都稳定有效。希望这套真题拆解对你有所帮助也欢迎把你的面试真题和解题思路拿来交流互相补全盲区。