携程数据开发笔试攻略:SQL窗口函数与数仓理论核心解析
1. 笔试整体结构与题型拆解1.1 在线笔试的形式与环节先说结论携程数据开发工程师的春招笔试第一批次整体走的是牛客网的在线笔试系统双机位监控手机扫码副机位全程录屏加切屏检测。整场笔试时间120分钟题量不大但陷阱不少最难受的是选择题和编程题混在一起交卷也就是说你不能先跳过选择题去做SQL必须按顺序一题一题往下走这一点和很多大厂的分模块交卷不太一样第一次遇到的人很容易在时间分配上翻车。从题目组成来看大致分为三块选择题含多选 两道SQL编程题 一道场景设计/简答题。选择题大概20道左右覆盖Java基础、Hadoop生态组件、数据仓库理论、Linux命令、Spark/Flink基础。SQL编程题一般是两道一道偏简单窗口函数排名类一道偏难连续登录、留存、区间重叠这类业务SQL。最后的简答题通常是给一个业务场景让你设计数仓分层方案或者排查数据倾斜属于综合能力题。这里要提醒一下笔试系统里面的编程题多数支持多种语言提交但你既然投的是数据开发千万别选Python硬刚用SQL提交通过率会高很多因为判题机的测试用例数据量不算太大SQL完全可以跑通而且携程的判题环境对Hive SQL和MySQL方言都兼容窗口函数也是支持的。我见过不少人用Python写半天结果因为读入方式和牛客的输入模板不对样例都没过属实亏。1.2 题型分布与分值逻辑我根据回忆整理了一下大致分值占比只做参考因为不同批次的题可能不完全一样但考法大方向是稳的。模块题量分值占比考察重点选择题约20题40%Java基础、Hadoop/Hive/Spark原理、Linux、数仓理论SQL编程题2题40%窗口函数、聚合计算、业务逻辑转换场景题/简答1题20%数仓建模、数据倾斜优化、任务调优思路从这个分值分布能看出来SQL写不出来基本就凉了选择题40分哪怕拿满两道SQL只要挂一道整体就很难过线。而这里说的“过线”往往不是及格线是简历池里的排序线。携程这种体量的公司笔试成绩是会分档的A档直接进面试B档进备选池C档基本就是感谢信。所以我的建议很直接准备重心放到SQL和场景题上选择题靠平时积累和刷题库保住基本功。SQL是硬通货场景题靠的是对数仓体系的理解选择题反而最不需要突击。很多人复习的时候本末倒置天天啃Java虚拟机结果SQL窗口函数写不顺最后笔试吃大亏。2. 核心考点深度解析SQL就是最大的分水岭2.1 窗口函数送分题还是筛选题携程的SQL题有一个很明显的偏好窗口函数必考。而且不是简单的row_number()排名往往会混合lag、lead、sum() over()、avg() over()这些函数一起用。我第一次做的时候感觉像是送分题因为窗口函数的语法背得很熟结果调试的时候才发现真正的坑不在函数本身而在分区字段和排序字段的选择。给你一个用户订单表要算每个用户最近三次下单的金额变化趋势看起来是典型的lag()场景但订单时间存在重复值如果不把order_id或者自增主键加进order by里窗口内的排序就是不稳定的同一个用户两次相同时间的订单可能随机交换位置结果就是趋势算出来乱跳。这里给大家一个我实际踩过坑后总结的经验order by字段里除了业务时间一定要带一个唯一性字段哪怕这个唯一性字段不参与业务计算只是为了打散窗口内行的顺序。-- 错误写法同一用户同秒下单时金额变化可能随机 select user_id, order_time, amount, amount - lag(amount) over(partition by user_id order by order_time) as diff from user_order; -- 推荐写法用order_id兜底保证窗口顺序稳定 select user_id, order_time, amount, amount - lag(amount) over(partition by user_id order by order_time, order_id) as diff from user_order;另一个高频考法是分组TopN这类题在牛客上刷过的基本都能写出来但携程喜欢换个马甲不是直接按部门分组取工资前三而是改成“按酒店维度取近30天销量前五的房型”“按航线分组取退票率最低的三个航班”。业务场景包装一下底层逻辑完全一样。你只要稳住row_number()的写法把分区字段和排序字段看清楚这道题基本上是稳的。真正拉开差距的是第二道SQL。2.2 复杂业务SQL连续登录、留存和区间重叠第二道SQL题我拿到的是一道连续登录相关的变体给定用户登录日志表包含user_id、login_date要求计算每个用户最大连续登录天数并根据连续天数区间进行分档统计。这题的经典解法是用row_number()做日期排序然后用login_date减去序号获得一个分组标记再按用户和分组标记聚合求天数。思路本身不复杂但携程在这里加了两个恶心的小点第一同一天多次登录要去重。登录日志表是明细级别的一个用户一天可能登录十几次如果你不去重直接开窗同一日期会被拆到不同行里连续性的判断就全乱了。这一步需要用distinct或者子查询先按user_id, login_date去重很多人挂在这一点跑出来全是1天。第二日期是字符串类型需要进行日期减整数的操作。在MySQL里直接date_sub(login_date, interval n day)没问题但如果你用Hive SQL的写法日期减整数会变成字符串拼接完全不是一回事。笔试环境到底支持哪种方言最好提前在本地把两种写法都练熟。-- Hive/通用SQL思路date_sub row_number with tmp as ( select user_id, login_date, date_sub(login_date, row_number() over(partition by user_id order by login_date)) as grp from ( select user_id, login_date from user_login_log group by user_id, login_date ) t ) select user_id, max(datediff(login_date, date_add(grp, 1)) 1) as max_continuous_days from tmp group by user_id;grp这个字段的含义是如果用户连续登录日期减去行号后的值保持不变一旦中断这个值就会变化。理解这个原理比背SQL重要因为老题会变体出新题比如“连续三天以上登录的用户数”“连续签到中断后重新开始计算”核心思想都是同一套。另一个携程喜欢考的方向是留存率。给你用户活跃表求次日留存、7日留存。常规解法是自关联按用户和日期配对后判断间隔天数然后按首日日期分组统计。这题看着简单但坑在数据量一大自关联的性能会很难看笔试判题机不一定超时但如果你是写MapReduce思路的那种写法在选择题里很容易被干扰项带偏。2.3 常考SQL题型总览与练习建议根据我个人的观察携程笔试SQL题基本锁定在以下几类里轮流出分组TopN结合row_number/rank/dense_rank要会区分三者去重逻辑连续问题连续登录、连续下单、连续签到本质是date_sub减行号留存分析次日/3日/7日留存本质是时间差统计行列转换多行转多列、多列转多行case when配合聚合是主流解法累计计算累计销售额、累计活跃用户数sum() over(order by ...)必考区间重叠同一用户时间区间合并这类题相对少见但一旦出现难度很高刷题平台的话牛客网的SQL题库足够但要注意牛客很多题是纯MySQL环境窗口函数语法没问题可日期处理和字符串处理函数会跟Hive有差异。建议刷题之外再花点时间把Hive常用函数过一遍重点看date_sub、datediff、from_unixtime、get_json_object这些因为携程很多业务数据是从日志解析来的JSON解析函数在场景题里也能用上。3. 数仓理论与Java选择题不能丢的保底分3.1 数仓理论考察的底层逻辑选择题里大概有5-8道是数仓理论相关不算多但都是实打实的基础分不需要你写代码白给的分数丢了很可惜。携程的数仓理论题喜欢考什么呢我总结下来就三块分层架构、维度建模、数据同步方式。分层架构的题一般是给你一个表的描述让你判断它属于ODS、DWD、DWS还是ADS层。比如“订单明细表经过清洗后按订单维度存储包含冗余的商品维度信息”这种就是典型的DWD层。如果你对分层的定位不够清晰很容易选错。这里有一个朴实但有效的记忆方式ODS是原样进来DWD是清洗成事实明细DWS是汇总成主题宽表ADS是面向应用的结果表。携程这个体量的公司还会考察主题划分和数据域划分的思想比如订单域、用户域、酒店域、交通域遇到这种题不要慌本质就是在考你对业务过程归类的理解。维度建模的题主要集中在星型模型和雪花模型的选择上。要记住星型模型是宽表冗余维度的查询性能好但不适合维度层级复杂的场景雪花模型是规范化维度的存储空间省但join次数多性能会下降。携程很多报表场景用的是星型模型加适当冗余考题问“为什么使用星型模型”时选“减少关联提升查询性能”基本没错。3.2 Java考点与大数据框架选择题第二个容易忽略的知识点是Java基础。数据开发工程师不是纯后端但笔试照样考Java这是很多人的知识盲区尤其是非科班转行的同学。选择题里的Java题不会太难基本停留在基本数据类型、集合框架、HashMap原理、String/Integer比较、异常处理这个层面。最经典的坑是Integer缓存像“Integer a 127; Integer b 127; a b返回什么”这种题懂JVM缓存的秒答不懂的想破脑袋想不通为什么128就不相等。这类题不用系统去啃Java教程直接刷牛客的Java基础题覆盖度就够了。大数据框架的题主要集中在Hadoop和Spark的基础原理上。HDFS的默认副本数、NameNode和DataNode的职责、MapReduce的Shuffle过程、Spark RDD的依赖关系、宽依赖和窄依赖的区别。这里有一个高频考点是Spark比MapReduce快的原因原因选项里有“基于内存计算”“DAG调度避免多次落盘”“多线程模型”“Task调度开销小”如果考多选前三个都要选。千万别选“Spark不需要Shuffle”那是错的Spark只是优化了Shuffle不是取消了Shuffle。Linux的题也会出现三五道比如crontab的语法、awk的用法、grep和sed的区别。这类题我有一个笨办法多刷两遍牛客Linux专项基本能覆盖到九成。整体来说选择题部分的复习策略就是广撒网用碎片时间刷题它不像SQL需要深度思考更考验的是你平时的知识面。4. 实操复习路线与笔试现场经验4.1 考前两周的高效复习安排很多人问笔试要不要专门复习我的答案是看你的SQL熟练度。SQL如果日常在刷考前两周足够如果大半年没碰SQL那至少提前一个月每天练。分享一下我个人的复习节奏。第一周集中刷SQL每天保证3道题覆盖上述六个题型。重点不是做对而是让自己的手指记得窗口函数的写法。很多人在笔试现场卡住不是因为不会而是因为函数名拼错、表名带特殊字符、逗号放错位置这种低级错误只能靠高强度练习来避免。我习惯用with语句把逻辑拆成一段一段先取中间结果再基于中间结果做下一步这样的好处是调试方便每段都能单独验证。第二周每天一套牛客厂真题模拟严格按120分钟计时。模拟的时候要注意选择题遇到不会的不要死磕先凭第一印象选一个继续往下走因为笔试系统不能回头修改。编程题如果5分钟内没有思路果断放弃半道先把第二道简单题完整做对再回来啃难题。这个策略在笔试里非常关键两道题各拿60%的分比一道题拿100%另一道0分要划算得多。另一个容易被忽略的准备工作是熟悉牛客的判题环境。有的SQL题判题机是单条语句校验你不需要create table只需要直接写select有的则要求先建表再插入数据再查询。这两种模式在牛客的题库里都能遇到提前搞清楚不然考场上会因为“环境模板对不上”白白浪费十分钟。4.2 笔试现场的时间分配与调试技巧真实的携程笔试时间是120分钟我的建议分配是选择题50分钟以内单题不超过2分钟第一道SQL20分钟第二道SQL35分钟场景题15分钟剩下时间检查这个节奏不一定适合所有人但我自己的体验是选择题如果超过50分钟后面的SQL大概率写不完整因为SQL题不只是写代码还要花时间读题理解业务逻辑。携程的SQL题业务背景都不短题目描述动不动就是两百字你要从里面抽取出表关系和指标口径这个过程本身就耗时间。写SQL的时候我强烈推荐分步写法。第一遍先把表join出来不加任何过滤条件先把中间结果跑出来看一眼第二遍再加partition by和聚合条件。不要一口气写完一长串SQL直接submit因为一旦报错排查的难度会指数上升。笔试判题机给了你多次提交的机会每次提交前的试探性运行都是宝贵的debug信息。还有一个很实用的技巧注意判题机的错误返回。如果是“运行超时”说明你的SQL在大数据量下存在性能问题可以尝试在子查询里先缩小数据范围比如先过滤掉无效状态或者只取近一年的数据避免全表扫描如果是“答案错误”多半是口径问题比如日期边界、空值处理、去重逻辑。结合错误类型来判断修改方向比肉眼debug高效得多。5. 高频坑点实录与复盘沉淀5.1 笔试必踩的三个SQL坑第一坑空值参与聚合计算。sum()和avg()会自动忽略NULL但count(字段)不会忽略NULLcount(*)计数所有行。你要统计“有下单记录的用户数”用count(user_id)还是count(distinct user_id)效果天差地别。携程的SQL题里经常埋这种空值判断的陷阱比如同一张订单表里面refund_amount字段可能是NULL你算退款率的时候如果直接用refund_amount / order_amountNULL除出来是NULL再套一层case when就全乱了。正确做法是先where过滤或者用ifnull/coalesce兜底。第二坑同名字段join后没有加表别名。这是最容易犯的低级错误一旦两个表都有create_time你不加别名直接写create_time判题机会直接报“字段不明确”。考场上我见过有人因为这种低级错误反复提交失败最后心态直接崩了。我的习惯是所有查询字段都带表别名哪怕是单表查询也带上这个习惯养成后很省心。第三坑group by后面字段必须和select的非聚合字段保持一致。有些题你想取“每个用户最近一次下单的时间和金额”直接group by user_id然后select user_id, order_time, amount在MySQL的严格模式下直接报错在Hive SQL里更严格不允许select非聚合字段。正确解法是用窗口函数先排序取行号再过滤或者用max()配合子查询关联。这个点非常高频笔试十次有八次能撞上。5.2 场景题作答的正确姿势场景题是携程笔试里最有区分度的一道题它不会让你写完整代码而是让你描述方案。比如某业务线的订单数据每天凌晨T1同步到数仓但今天发现数据量比昨天少了20%可能是什么原因如何排查这类题其实没有标准答案面试官看的是你的排查思路是否清晰、是否成体系。我的作答框架是先定位问题层级再逐层排查。首先查同步任务本身确认上游数据源是否有问题比如业务库binlog是否正常写入、同步任务是否失败重跑其次查ODS层源表是否有分区数据丢失数据是否被误删然后查DWD/DWS层是否有过滤条件被变更、是否有去重逻辑被误改最后查消费端是否有人改过报表指标口径。把这四个层级讲清楚再加一句“可以对比前几天同周期的数据量曲线以及和昨天各分区的行数进行明细比对”这个答案基本就完整了。场景题还有一类容易考到的是数据倾斜处理。比如让你描述一个大Key导致Reduce阶段卡住怎么办。回答的思路要包含先定位倾斜Key可以通过group by后count排序来找热键然后提出方案常见的有加盐给倾斜Key打散、两阶段聚合、调整spark.sql.shuffle.partitions参数、用广播变量替代大表关联。不需要你把每种方案都写得很细但要把关键点列全让面试官觉得你确实是处理过真实问题的。5.3 笔试后的复盘与面试衔接笔试结束不等于万事大吉。我每一次笔试结束后都会做一次完整的复盘把当时没做出来的题重新写一遍把选择题里不确定的知识点查找一遍全部整理成笔记。因为笔试里暴露出来的薄弱点在面试里大概率会被再次问到。比如你笔试里SQL窗口函数没写对面试官如果手上有你的笔试记录很可能上来就问“你笔试里有一道窗口函数的题现在能不能再写一遍”这种现场翻旧账的操作其实并不少见。复盘还有一个作用让自己形成错题集。数据开发笔试的知识点非常琐碎Java的Integer缓存、Linux的awk语法、Spark的宽窄依赖、数仓的分层理论这些内容彼此独立不形成错题集的话很容易在正式面试前忘干净。我用的是最普通的笔记方式每个知识点一行关键结论加一个例子考前翻一遍就能快速回忆。这套方法帮我省下了大量重复复习的时间也让我在后续的面试中底气更足。携程的笔试整体难度在一线互联网公司里属于中等偏上它不考偏题怪题但非常注重基本功的扎实程度。只要你把SQL写熟练、把数仓体系理清楚、把Java和大数据框架的基础题刷到位通过笔试的把握是很大的。春招这一路下来我的感受是笔试的筛选效率非常高它确实能在短时间内区分出有没有真实项目经验的人。准备的过程虽然枯燥但每一道题练扎实了后面面试时都会变成你的底气。