当前位置: 首页 > news >正文

面试录音复盘:SQL 去重被追问到卡壳,distinct / group by / row_number 到底差在哪?

听完这段面试录音,我第一反应不是嘲笑,而是“太典型了”。

候选人简历写得很漂亮:项目是“物联网设备 + 实时数据 + 数仓体系”,架构也能说两句。但面试官一旦开始追问SQL 去重这种基础点,就会瞬间暴露:知道关键词,不知道机制;会写片段,不会把逻辑讲完整

这篇我按“录音复盘”的方式,把其中几个最致命的坑拆开讲清楚,顺便给出一套可背诵的回答模板常见追问清单

说明:内容来自面试录音的脱敏改写,不对应任何特定个人与公司,仅用于学习复盘。

你看完能带走什么

  • distinct / group by / row_number去重:各自适用场景、风险点、怎么说才像做过大数据
  • 窗口函数三兄弟rank / dense_rank / row_number的区别怎么一句话讲清
  • Left Join 条件写 on 还是 where:为什么一句位置写错就“左连接变内连接”
  • JSON 字段解析:ODS/明细层常见写法的性能差异与更稳的方案
  • 顺带补一个:离线 vs 实时架构怎么讲才不显得“搭积木”

一、离线 vs 实时:别把架构当积木乱搭(更像项目的讲法)

录音里候选人大意是:同一份数据走两路——一路进 Hive 做离线,一路进 Kafka 做实时。

听上去像是“离线一套、实时一套”,但项目里常见的问题是:入口采集做成两套,后面会带来维护成本、对源端压力、口径不一致、回放困难等一堆麻烦。

更稳、更像面试答案的表达是:

  • 一次采集,多次分发:先把数据统一进入一个“总管道”(常见就是 Kafka)
  • 实时链路:Flink 消费 Kafka → 实时计算 → 下游(OLAP/ES/指标服务)
  • 离线链路:Flink/采集任务消费 Kafka → HDFS/Hive → 数仓分层与离线指标

这样讲,面试官听到的是:你知道解耦、回放、削峰、口径统一、可重算这些真实项目关键词。


二、SQL 去重:distinct、group by、row_number 不是“差不多”

面试官问:“你怎么给数据去重?”
候选人答:“用 distinct,或者 group by……还有那个 row_number。”
面试官追问:“这仨有啥区别?哪个快?”
候选人:“呃……差不多吧?”

这题看似基础,实际上在考你两件事:执行代价意识+业务语义是否说清

2.1 distinct:写起来爽,代价有时很重

select distinct user_id from orders;

在 Hive/SparkSQL 等引擎下,distinct往往会带来大规模 shuffle + 聚合。数据量大、分布不均时,容易出现长尾/倾斜,表现就是任务长时间卡进度。

面试里更稳的说法:

  • 我会先评估数据量、分区、是否能先过滤
  • 再决定用 distinct,还是换更可控的聚合/窗口方案

2.2 group by:更容易配合“先过滤、再聚合”

select user_id from orders group by user_id;

很多场景下,group by更便于你讲清楚:先 where 过滤、再按 key 聚合,并且在一些引擎/配置下会有更好的聚合路径(例如 map 端聚合等优化)。

2.3 row_number:它不是去重本身,是“打排名”,去重在最后一刀(重点)

这是最容易露馅的点:有人以为写了row_number()数据就“自动去重”,其实它只是把数据编号(1,2,3…),不会删除任何一行。

典型需求:每个用户保留最新一条订单

select t.user_id, t.order_time, t.amount from ( select user_id, order_time, amount, row_number() over(partition by user_id order by order_time desc) as rn from ods_order_table where dt = '2023-10-24' -- 必须带分区过滤(非常关键) ) t where t.rn = 1; -- 这一步才是真正的“去重动作”

你在面试里要表达清楚三点:

  • 我按user_id分组,按时间倒序
  • row_number给每组打排名
  • rn=1才是保留最新记录
加分点:如果order_time可能相同,补一句“需要再加稳定排序字段(如自增 id/写入时间)保证结果确定性”。

深度思考(可选加分讲法)

  • row_number很适合TopN取最新状态状态变更流
  • 数据极大且 key 过热时,单纯窗口也可能有单 key 压力,高阶优化才会聊到“加盐/双段聚合/在流计算侧做状态治理”等

三、窗口函数三兄弟:rank 到底跳不跳?(一句话背诵版)

面试官问:“rankdense_rankrow_number有啥区别?”

  • row_number():不并列、不跳号。哪怕分数一样,也是1,2,3。适合 TopN / 取最新(不管并列)
  • rank():并列、跳号。比如两个第一名,下一个是第三名:1,1,3
  • dense_rank():并列、不跳号。两个第一名,下一个还是第二名:1,1,2

记忆法:dense=稠密,数字挤在一起,不留空位,所以不跳号


四、Left Join 的“鬼故事”:where 和 on 别乱放

面试官问:“A left join B,我想过滤 B 表的数据,条件写在 on 里还是 where 里?”

很多人觉得没区别,但这直接决定了你是在做Left Join还是不小心变成Inner Join

4.1 常见灾难写法(Left Join 退化成 Inner Join)

select * from orders a left join users b on a.user_id = b.user_id where b.age > 18;

为什么错?

  • 你本意是保留左表(orders)所有数据,即便右表匹配不到也保留
  • 但匹配不到时b.agenull
  • null > 18结果不是 true,于是这部分行被 where 过滤掉
  • 结果:Left Join 直接变成“像 Inner Join 一样”的效果,数据量就对不上

4.2 更稳的正确写法(将条件放在ON子句中)

--将条件放在ON子句中 select * from orders a left join users b on a.user_id = b.user_id and b.age > 18; -- 注意这里是AND,不是WHERE!

你在面试里这么讲更加分:

  • 我想保留左表全量,所以过滤右表要放在子查询on 条件
  • 要真正过滤结果,用WHERE

深度思考(可选加分讲法)

  • 大表 join 大表要注意 shuffle 代价
  • 若过滤后右表足够小,可以讨论广播/MapJoin(具体阈值与引擎/参数相关,面试不必报死数字)

五、JSON 解析:ODS 层的高频坑(别一字段一解析)

面试里提到 JSON 字段解析。很多新手会这样写:

select get_json_object(json_str, '$.id'), get_json_object(json_str, '$.name') from table_name;

问题在于:你取多个字段时,可能会反复解析字符串,CPU 成本会很高。

5.1 Hive 场景更稳:json_tuple(一次拆多字段)

select jt.id, jt.name from table_name lateral view json_tuple(json_str, 'id', 'name') jt as id, name;
  • 一次拆多个字段
  • 语义更清晰,项目里也更好维护

5.2 Spark 场景更推荐:from_json + schema(可选补充)

  • 如果你面试的是 SparkSQL/湖仓相关岗位,可以补一句:
    “Spark 侧更推荐from_json定 schema,一次解析且类型安全。”

总结:这段面试录音暴露的 3 个根因

  1. 简历可以优化,但“技术细节”经不起追问
  2. SQL 别光会写,要懂机制与代价:distinct 为什么慢、窗口函数到底干了什么、Left Join 条件位置为什么会改语义
  3. 要有大数据思维:默认数据量大,必须考虑分区过滤、倾斜、shuffle 代价、可重跑与幂等等项目要点

面试官高频追问清单(建议收藏)

  • row_number取“最新一条”时,排序字段不唯一怎么办?
  • distinct / group by 在你的引擎里各自可能的代价是什么?什么时候你会避免用 distinct?
  • Left Join 的过滤条件为什么写 where 会变味?如果必须写 where,怎么写才不误伤?
  • JSON 字段很多时,怎么避免重复解析?Hive 和 Spark 的推荐写法分别是什么?
  • 这条链路怎么保证幂等、可重跑、补数?延迟数据/晚到数据怎么处理?

如果你也在准备数据仓库 / 数据开发 / 报表开发面试,我建议你先把这 5 个点练到“能讲清楚”。
我后面会持续用“面试录音复盘”的方式,把高频问题拆成可直接复用的回答模板。

http://www.jsqmd.com/news/567093/

相关文章:

  • 揭秘Cuvil官方未文档化的--enable-unsafe-fp16标志:实测提速33%但引发梯度爆炸的隐藏代价
  • 钧略AIGEO:以专业AI搜索优化 打造企业智能获客新引擎 - 企业推荐官【官方】
  • 如何高效配置Windows安卓子系统:完整的专业开发指南
  • Kong Manager 实战指南:从安装到配置全流程解析
  • 时间序列形态识别:chan.py框架在工业传感器数据分析中的应用指南
  • 保姆级教程:手把手教你用Vue 3 + TypeScript封装一个媲美Element UI的Slider滑块组件
  • 铁路安全新利器:TWDS系统如何用CCD技术实时检测轮对故障?
  • ROCmLibs-for-gfx1103:解锁AMD 780M APU 2-3倍AI性能的终极优化方案
  • 记录一次 反射引起的Metaspace OOM 的完整排查
  • 终极AMD Ryzen调试指南:使用SMUDebugTool轻松优化你的处理器性能
  • MIKE URBAN前处理之ArcGIS批量拆分属性表中的字段
  • StructBERT零样本分类-中文-base行业落地:医院在线问诊首句意图识别(挂号/复诊/报告查询)
  • “因果森林+双重稳健估计”强强组合,这篇文章代表着2026年医学因果推断方法学趋势
  • 感应电机有/无传感器控制FOC带文档 感应电机有/无速度传感器FOC控制,异步电机有/无速度传...
  • 告别手动填表!用CANoe 11.0 (x64)模板快速创建DBC数据库(附Signal/Message避坑指南)
  • 基于博途1200PLC与HMI的十层三部电梯控制系统仿真程序
  • SDMatte在数字政务中的应用:证件照/公章/红头文件透明底标准化处理
  • 又一体脂肪指数类指标上线NHANES公共数据库平台---锥度指数
  • 从模糊到逼真:VAE-GAN如何用‘学来的相似度’解决VAE的图像模糊问题?
  • HPKM-PINN:KAN-MLP并行混合物理信息神经网络技术 第1章 KAN基础与MLP局限的理论分析(一)
  • Hunyuan-MT-7B多场景应用:Pixel Language Portal赋能高校外语教学平台的AI助教落地案例
  • 反逻辑陷阱:写机器无法理解的荒诞代码
  • IF=22.3!三臂临床试验的统计方法拆解:八段锦降压研究的顶刊设计思路借鉴
  • G-Helper终极指南:释放华硕笔记本全部潜力的轻量级控制工具
  • Windows驱动管理新范式:DriverStore Explorer从入门到精通
  • 基于单片机多功能音乐门铃录音留言箱
  • STM32G030C8T6 + DRV8833 驱动42步进电机:从零到64细分的保姆级代码解析
  • DFT工程师的隐藏技巧:深入解读TestMAX中Shared与Dedicated Wrapper Cell的选择策略
  • Servlet02---超详细的HttpServlet讲解
  • 实测分享:用Metashape(原PhotoScan)从无人机照片到3D模型的全流程避坑