ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数据库SQL面试高频73题:从基础查询到窗口函数全攻略

数据库SQL面试高频73题:从基础查询到窗口函数全攻略 金三银四那会儿我帮一个转行的朋友做模拟面试随手抽了两道SQL题。第一道是“连续登录N天”他上来就自连接加去重写了三十多行勉强跑通第二道是“算每个品类的销售额累计占比”他直接愣住了。其实这两道题在我整理的这套数据库SQL面试高频73题里都排得上号考察的都不是花哨语法而是对数据粒度、执行顺序和业务口径的理解。这套题我从三年前开始维护每一次面试复盘、每一份面经里的真题我都会归并去重后收进去目前固定更新到73道。覆盖了基础查询、聚合分组、多表连接、窗口函数、日期与字符串处理、数据清洗、慢SQL优化和SQL安全基本就是数据分析、商业分析、数据运营岗位笔试和一面手撕SQL的全部射程。如果你正在准备面试或者入行两三年想自查一遍基本功这篇内容可以当作索引来用。73道题按模块分布如下后面我会挑每一类里最容易失分的重点题展开讲完整题集可以按这个结构对号入座地去刷。模块题数核心考察点基础查询与筛选8题WHERE、NULL陷阱、排序分页、逻辑执行顺序聚合分组与去重10题GROUP BY粒度、HAVING、COUNT与DISTINCT、清洗场景去重多表连接8题JOIN类型、连接条件、数据膨胀、驱动表子查询与CTE6题EXISTS与IN、派生表、递归CTE窗口函数12题排名三兄弟、分组TopN、连续登录、同比环比日期、字符串与数据清洗10题日期函数、格式转换、脏数据修复、身份证手机号清理性能优化与SQL安全8题EXPLAIN、索引失效、慢SQL答题框架、参数化查询综合业务题11题留存、漏斗、复购率、用户分层等业务场景SQL1. 73题的构成逻辑面试官真正在考察的四个维度1.1 为什么是73题而不是30题或200题我在整理题集时有个原则只收真题不收语法手册。市面上面试题库动辄几百上千道但大量是重复考察同一个知识点比如“查询第二高的薪水”至少有五种变体本质上考的是LIMIT/OFFSET或窗口函数刷十道不如刷透一道。73道这个数量是我结合面试场景倒推出来的。一次典型的数据分析面试手撕SQL环节通常集中在40分钟到1小时能考察的题目数量有限。面试官不会出一道纯考记忆的题他只会选那些能暴露你思维过程的题。所以我在筛选题目时做了三层过滤第一层是出现频率至少在三份不同公司的面经里出现过的题才收录第二层是区分度一道题如果背下来就能过不要第三层是可扩展性一道题必须能延伸出两到三个追问例如从“分组TopN”能追问“同分怎么排”“数据量大了怎么办”。1.2 数据分析SQL面试的“四层考察模型”我复盘了大量面试录音和反馈后发现面试官考察SQL题时脑子里其实有一个隐性的评分框架可以拆成四层。第一层是语法正确性能不能写出能跑通的代码这是门槛。第二层是逻辑清晰度WITH语句、缩进、别名是否规范子查询嵌套有没有滥用这反映了你平时写代码的习惯。第三层是业务敏感度同一张订单表“每个用户的首单时间”和“每个用户最近的未支付订单”写法完全不同能不能识别出背后的业务语义。第四层才是性能意识会不会提索引、能不能识别数据倾斜这是区分中级和高级的标尺。这四层不是一个一个去过的而是同一道题里同时被考察。比如“求连续登录N天”这道题最基础的解法是自连接能跑但性能差进阶解法是用窗口函数算日期与行号的差值代码简洁再往上面试官会追问你“如果登录记录有重复怎么办”这就要求你先把去重逻辑写对。从自连接到窗口函数再到去重一道题就把四层都考遍了。1.3 收藏之后该怎么刷我见过太多人收藏了题集就放进收藏夹吃灰等到面试前一周才开始慌。我的建议是分三轮刷。第一轮按模块刷每天一个模块只求搞懂每一道题的解题思路不要求手写。第二轮合卷刷随机抽取20道题限时手写写不出来就回看题解这一轮的目的是暴露薄弱点。第三轮是场景模拟把题集里的题目改编成自己简历里的业务数据去写比如你把“用户订单表”换成你项目里的“电商订单表”能写出同一道题才说明你真的吃透了。这套方法我带过的人验证下来三周左右能把基本功补扎实。2. 基础查询与筛选最容易被扣分的隐藏细节2.1 WHERE的求值顺序一道题暴露你的基本功基础查询模块里我第一道题就会问SQL的逻辑执行顺序。很多人张口就是“先SELECT后FROM”这个答案在面试官那里等于直接扣分。一条SQL的逻辑执行顺序是FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。注意这里的“逻辑顺序”不等于数据库实际执行的物理顺序优化器会做重排但逻辑顺序决定了你写代码时思考的路径。为什么这个顺序这么重要因为很多报错和逻辑错误都源于没有理解它。比如你在WHERE里引用SELECT子句里定义的别名数据库会报“Unknown column”因为WHERE在SELECT之前执行别名还没生成。又比如ORDER BY可以用别名因为它在SELECT之后执行。这些细节笔试不会直接问“执行顺序是什么”但会通过一道“找错题”来考察你比如-- 这段代码哪里有问题 SELECT user_id, COUNT(*) AS order_cnt FROM orders WHERE order_cnt 3 GROUP BY user_id;正确答案是WHERE里不能使用order_cnt这个别名因为WHERE先于SELECT执行。这个错误我面试过的人里有一半会看漏。改法是加一层子查询或者改用HAVING。这道题本身不难但在紧张的面试环境里一眼定位问题需要扎实的基本功。2.2 NULL参与计算的三个坑基础查询里还有一个高频考点是NULL。NULL不是0不是空字符串它是“未知”。很多数据分析师在工作里被NULL坑过面试题里自然也爱考。第一个坑WHERE col admin不会把col IS NULL的行查出来。因为NULL与任何值比较结果都是NULL而非TRUE只有IS NULL能判断。这一点在筛数时很容易导致数据缺失我在项目里就遇到过筛选“非VIP用户”时把一批用户类型为NULL的用户漏掉了导致运营活动少发了一批优惠券。第二个坑COUNT(列)会自动忽略NULLCOUNT(*)不会。如果统计“有多少用户填了手机号”用COUNT(phone)是对的如果统计“订单总数”用COUNT(*)更稳妥。很多人习惯统一用COUNT(*)在某些场景下就会得到错误的指标。第三个坑NULL与NOT IN的组合。WHERE id NOT IN (SELECT user_id FROM blacklist)时如果黑名单子查询结果里有任何一行user_id为NULL整个查询结果就是空。原因是NOT IN本质上是一系列的比较遇到NULL会变成“未知”最终一行都匹配不上。这题的替代方案是用NOT EXISTS它的语义是先找匹配行再取反不受NULL影响。2.3 排序与分页笔试里最容易大意的细节排序题的坑在NULL值的排序位置。MySQL里ORDER BY col ASC默认NULL排在最前Oracle里默认NULL排在最后SQL Server也是NULL排在最前。同一个逻辑不同数据库结果不一样。面试时如果题目没有明确指定数据库我建议先说明“MySQL默认NULL在ASC时排最前需要把NULL统一处理的话用IS NULL单独控制”这样既展示了知识面又避免了答案在特定环境下出错。分页的坑在LIMIT的偏移量。LIMIT 10 OFFSET 10000这个写法在数据量小的表上没问题但数据量一大数据库要扫描并丢弃前10000行才能返回第10001行到第10010行越到后面的页越慢。面试官如果追问“如何优化深分页”核心思路是不要用大OFFSET而是用上一页的最后一条记录的ID做条件定位到下一页或者用子查询先查出起点ID再取数据。这个优化思路要比单纯背语法重要得多。3. 聚合、分组与去重指标口径才是数据分析师的护城河3.1 GROUP BY的粒度问题先想清楚行是什么聚合查询是数据分析师每天写最多的SQL。73题里我设置了10道但核心考点就一个先定义粒度再写聚合。举个例子题目是“统计每个用户的累计支付金额”订单表orders里每个用户有多条订单那么GROUP BY user_id后的每一行代表一个用户聚合字段是SUM(amount)这个粒度清晰明确。但如果题目是“统计每天每个品类的销售额”表里同时有order_id、product_id、category、amount、order_date那么分组键是order_date, category每一行代表“某天某品类”。很多人会漏掉日期只按category分组那就把多天的数据揉在一起了粒度完全不对。我在面试时经常追问一句“这张表里一行代表什么”这个问题能过滤掉一大半只会套模板的人。同样一段SQL在订单明细表、用户签到表、商品浏览日志表里一行的含义完全不同GROUP BY的结果自然也完全不同。理解粒度是写出正确聚合的前提。3.2 HAVING与WHERE先过滤再聚合 vs 先聚合再过滤HAVING和WHERE的区别表面上是“WHERE过滤行HAVING过滤组”但面试官真正想听的是执行顺序层面的理解。-- 找出订单数大于3次且2024年之后有下单的用户 SELECT user_id, COUNT(*) AS cnt FROM orders WHERE order_date 2024-01-01 -- 先过滤日期范围减少进入聚合的数据量 GROUP BY user_id HAVING COUNT(*) 3; -- 再过滤聚合后的组如果把这个WHERE条件误写成HAVING在逻辑上行得通但性能会更差因为HAVING是在聚合之后才过滤会导致大量无用行参与了聚合。这个细节笔试不会直接考但代码review和排查慢SQL时很容易成为性能瓶颈。另外要注意的是WHERE里不能写聚合函数比如WHERE SUM(amount) 100直接报错原因还是执行顺序WHERE执行时聚合还没发生。如果一道面试题问“为什么WHERE里不能用聚合函数”你从执行顺序答面试官会觉得你是真懂了。3.3 数据清洗场景的去重SQL保留一条的完整写法去重题在数据分析面试里占比很高因为真实工作中数据清洗的第一步往往就是去重。73题里我专门收录了“删除重复数据保留每个用户最早的一条记录”这类题。先看一个容易踩坑的写法-- 这种写法在MySQL里会报错不允许对目标表同时进行SELECT和DELETE DELETE FROM users WHERE id NOT IN ( SELECT MIN(id) FROM users GROUP BY email );正确做法是先把要保留的ID查出来放到临时表或派生表里再执行删除DELETE FROM users WHERE id NOT IN ( SELECT keep_id FROM ( SELECT MIN(id) AS keep_id FROM users GROUP BY email ) t );如果还想统计重复数量可以用窗口函数先标记再按标记删除这种方式在清洗时更可控-- 1. 先看看每个邮箱有多少条记录 SELECT email, COUNT(*) AS cnt FROM users GROUP BY email HAVING COUNT(*) 1; -- 2. 用窗口函数保留每组最早的一条 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at ASC) AS rn FROM users ) t WHERE rn 1;这套“先探查、再标记、后处理”的思路在真实数据清洗项目里非常实用。面试官往往不只想听你写DELETE还想听你如何验证去重结果、如何备份原始表数据这些实操经验比一句SQL值钱。4. 多表连接与子查询把业务问题翻译成JOIN和子查询4.1 JOIN类型与连接条件笛卡尔积是怎么来的多表连接部分我最爱问的一道题是“一张10万行的订单表和一张1万行的用户表做LEFT JOIN结果最多可能有多少行为什么”这不是考数学而是考对一对多关系的理解。如果用户表里存在重复用户ID比如同一个user_id被录入了两次那么订单表中每个该用户的订单都会和用户表里的两行匹配结果行数就会膨胀。如果订单表本身有10万行用户ID去重后是1万但用户表里有100个重复ID结果行数就会超过10万。这种“连接膨胀”在工作中最直接的危害是SUM一个指标时数据被重复计算了导致报表数字翻倍。我见过一个电商项目因为用户表里有历史重复数据没有清理导致CTR计算分母翻了两倍业务方差点按错误数据做了投放决策。所以面试时只要涉及JOIN我建议先看一眼两张表的粒度确认关联键是否唯一。口诀是关联键在一边重复结果行数就会膨胀重复在“多”的那一边膨胀程度最大。这个习惯能在源头上避开很多报表事故。4.2 LEFT JOIN后右表条件的位置刚学的人最容易踩的坑还有一个高频考点是“LEFT JOIN WHERE后左连接为什么失效”。-- 这个查询真的能保留所有未支付订单吗 SELECT o.order_id, p.pay_time FROM orders o LEFT JOIN payments p ON o.order_id p.order_id WHERE p.pay_time IS NOT NULL;大多数人一眼看去会觉得“左连接嘛左边全保留”但WHERE里加了p.pay_time IS NOT NULL这个条件后右表为NULL的行就被过滤掉了整个查询实际变成了INNER JOIN。如果题目是“找出所有下单但未支付的用户”正确写法应该在JOIN条件里过滤或者把右表条件放在子查询里SELECT o.order_id, o.user_id FROM orders o LEFT JOIN ( SELECT order_id, pay_time FROM payments WHERE pay_time IS NOT NULL ) p ON o.order_id p.order_id WHERE p.order_id IS NULL;这道题几乎每次面试都会有人掉坑里。核心在于理解JOIN条件里的过滤是先过滤再连接WHERE里的过滤是连接后再过滤。这两种过滤的执行时机不同结果完全不同。4.3 从两表关联到留存漏斗业务SQL题的拆解套路笔试里常见的业务题比如“计算次日留存率”“统计每个漏斗环节的转化率”本质都是多表连接和聚合的组合。以次日留存为例第一步拆解业务含义用户在第0天活跃过第1天是否再次活跃。所以我们需要把活跃表按日期错开一天做自关联。-- 活跃表user_id, active_date -- 次日留存率 第1天仍活跃的用户数 / 第0天活跃用户数 SELECT a.active_date, COUNT(DISTINCT a.user_id) AS active_users, COUNT(DISTINCT b.user_id) AS retained_users, COUNT(DISTINCT b.user_id) / COUNT(DISTINCT a.user_id) AS retention_rate FROM active_log a LEFT JOIN active_log b ON a.user_id b.user_id AND b.active_date DATE_ADD(a.active_date, INTERVAL 1 DAY) GROUP BY a.active_date;这里有两个关键点。第一关联条件里用DATE_ADD实现“次日”的日期对齐而不是在WHERE里手动写死日期。第二统计留存用户数用COUNT(DISTINCT b.user_id)而非COUNT(b.user_id)因为左表的一行可能对应右表的一行但右表里也可能有重复活跃记录不去重会把留存人数算多。做完这道题面试官大概率会追加“如果要求7日留存呢”改关联条件为DATE_ADD(a.active_date, INTERVAL 7 DAY)即可逻辑完全一样。这就是典型的一道题覆盖一类题值得花时间吃透。5. 窗口函数73题中的分水岭四种高频场景解析5.1 窗口函数的基本结构与执行顺序窗口函数是数据分析面试的分水岭。SQL基础考察的是你会不会写窗口函数考察的是你有没有处理复杂数据分析需求的能力。我在题集里放了12道窗口函数题占全册六分之一可见其重要性。窗口函数的基本结构是聚合函数/专用函数 OVER (PARTITION BY 分组字段 ORDER BY 排序字段 ROWS/RANGE 窗口边界)。跟GROUP BY最大的区别在于GROUP BY会压缩行数每一组只剩一行窗口函数不会压缩行数每一行仍然保留只是在每一行上多计算了一列。执行顺序上窗口函数在WHERE、GROUP BY、HAVING之后执行也就是在SELECT阶段计算。这意味着你不能在WHERE里引用窗口函数的结果。很多人在笔试里写出WHERE rn 1直接报错就是没理解这一步。5.2 排名三兄弟一道题问出你到底是背还是懂排名题几乎没有一次面试缺席而且面试官一定会追问RANK、DENSE_RANK、ROW_NUMBER的区别。这三者的差异在遇到并列名次时才体现函数相同分数排名下一个名次典型场景ROW_NUMBER()随机/按ORDER BY顺序编号不并列连续分页、取固定行数RANK()并列占用名次跳跃如1,1,3并列名次且占位DENSE_RANK()并列不占名次连续如1,1,2排行榜名次比如按销售额对销售员排名SELECT salesperson, amount, ROW_NUMBER() OVER (ORDER BY amount DESC) AS row_num, RANK() OVER (ORDER BY amount DESC) AS rank_num, DENSE_RANK() OVER (ORDER BY amount DESC) AS dense_num FROM sales;数据为100、100、80时三列结果分别是(1,2,3)、(1,1,3)、(1,1,2)。我建议你把这道题亲手跑一遍因为只看不写永远记不住这三者的差异。面试时能把“占用名次”和“不占用名次”说清楚的说明是真在做数据分析不是在背题库。5.3 高频场景二分组TopN的完整解法题目“求每个品类销量排名前三的商品。”SELECT category, product_id, sales_cnt FROM ( SELECT category, product_id, sales_cnt, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales_cnt DESC) AS rn FROM product_sales ) t WHERE rn 3;这道题的解题关键有三个。第一PARTITION BY决定分组第二内层查询生成排名第三外层查询过滤排名。很多新手会忘记外层查询直接在SELECT里写了ROW_NUMBER再加WHERE必然报错原因就是窗口函数在WHERE之后执行必须先包一层子查询。延伸追问往往是“销量相同怎么取”。如果要求并排名次把ROW_NUMBER换成RANK即可如果要求每个品类严格取三条哪怕并列也不多取保持ROW_NUMBER。这个追问考察的是你对业务需求的理解数据量不同、场景不同答案不同没有绝对的对错。5.4 连续登录、累计求和、同比环比窗口函数的三张王牌连续登录N天是窗口函数题里最经典的一道没有之一。我见过自连接写法、笛卡尔积写法但最优雅的是“日期减行号”分组法。解题思路分四步第一步对每个用户的登录日期去重因为一天可能有多条登录记录第二步用ROW_NUMBER按用户分组给日期编号第三步用登录日期减去编号对应的天数得到一个“分组标志”连续登录时这个标志不变第四步按用户和标志分组统计天数。SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL rn DAY) AS group_flag FROM ( SELECT user_id, login_date, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY login_date ) AS rn FROM ( SELECT DISTINCT user_id, login_date FROM login_log ) t1 ) t2;得到group_flag后按user_id, group_flag分组统计次数天数大于等于3的就是连续登录3天及以上的用户。这个解法的巧妙之处在于日期和行号是同步增长的如果中间断了两者的差值就会变化正好用这个差值做分组依据。同样高频的还有累计求和和同比环比。累计求和是SUM(amount) OVER (ORDER BY date ASC)按日期累加销售额。同比环比是LAG和LEAD函数LAG(amount, 1) OVER (ORDER BY date ASC)取上一期数据LEAD(amount, 1)取下一期然后做百分比计算。-- 每月销售额的环比增长率 SELECT month, amount, LAG(amount, 1) OVER (ORDER BY month) AS prev_amount, (amount - LAG(amount, 1) OVER (ORDER BY month)) / LAG(amount, 1) OVER (ORDER BY month) AS mom_growth FROM monthly_sales;窗口函数在Hive SQL、Spark SQL、MySQL 8.0、SQL Server、Oracle、PostgreSQL里都原生支持。如果你工作中已经在用Python的pandas做数据分析和可视化那么窗口函数你大概率在DataFrame的groupbytransform里已经遇到过类似逻辑SQL只是换了一种表达方式上手会非常快。6. 日期、字符串与数据清洗笔试现场的真实翻车点6.1 日期函数DATE_FORMAT、DATEDIFF、DATE_ADD的实际边界日期处理在SQL面试里占了10道题绝不是考你背函数名而是考你能不能根据业务要求选对函数。有三类高频日期题。第一类是格式转换比如DATE_FORMAT(order_date, %Y-%m)把2024-03-15转成2024-03。这里有个坑MySQL的格式化占位符是%Y-%m-%d而SQL Server用的是FORMAT(order_date, yyyy-MM)Hive用的是date_format(order_date, yyyy-MM)。同一个函数不数据库间语法差异不小写之前先明确环境。第二类是日期差计算DATEDIFF(2024-03-15, 2024-03-01)返回14。但要注意不同数据库的DATEDIFF参数顺序不一致MySQL是DATEDIFF(日期1, 日期2)SQL Server是DATEDIFF(间隔单位, 日期1, 日期2)Hive是datediff(2024-03-15, 2024-03-01)。面试时可以主动问一句“这个环境用的是哪个数据库”问出来比闷头写强得多。第三类是日期偏移DATE_ADD(date, INTERVAL 1 DAY)是取后一天DATE_SUB是前一天。计算“近7日”时很多人习惯直接WHERE date CURDATE() - 7但如果date字段是带时分秒的DATETIME这个写法就不严谨正确做法是WHERE date DATE_SUB(CURDATE(), INTERVAL 7 DAY)把边界条件写清楚。6.2 字符串清洗身份证、手机号、金额格式的实战处理字符串处理在面试里最经典的翻车点是“科学计数法”。很多面试官会出一道实际工作中踩过的坑题“从Excel导出的身份证号变成了科学计数法怎么用SQL清洗恢复”这个问题的本质是Excel对超过11位的纯数字会自动转成科学计数法比如身份证号110101199003071234变成1.10101E17精度丢失。如果数据已经丢失了SQL无能为力只能重新从源系统导出如果原始文件还在正确做法是用文本方式导入或者先把Excel单元格格式设为“文本”再输入。如果数据源是Oracle、SQL Server等数据库导出的导成CSV时要指定文本格式不要用Excel直接打开。SQL层面的处理是预防和校验判断手机号码、身份证号字段有没有被科学计数法污染。比如用LENGTH(phone) 11校验手机号位数或者用正则去匹配身份证的18位模式。如果发现位数不对则说明源头导出环节出了问题而不是SQL里写个FORMAT能解决的。这道题的原型是“如何正确显示身份信息”本质上考察的是你对数据链路的理解而不仅仅是SQL语法。其他的字符串高频题还包括SUBSTRING_INDEX截取、CONCAT拼接、REPLACE清洗换行符、REGEXP_REPLACE正则清洗手机号中间四位做脱敏。-- 手机号脱敏138****5678 SELECT CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS masked_phone FROM users;6.3 空值与脏数据的处理策略数据清洗题的最后一类考点是脏数据处理策略。面试官给一张包含NULL、空串、格式不统一的表问你如何清洗。我的通用答题框架是三步。第一步探查用COUNT和COUNT(列)的差算出每列的缺失量观察缺失比例第二步分类区分NULL、空字符串、不可见字符比如和NULL要分别处理 张三 和张三要统一TRIM掉空格第三步制定规则缺失率低的行直接过滤缺失率高的字段考虑默认值或上游补数身份证、手机号等关键字段校验格式异常。这道题面试官想听的往往不是某个具体函数而是你有没有一套清洗方法论。我的建议是哪怕时间紧也要把“先探查、再分类、后处理”的逻辑讲全这比直接写语句更能体现数据分析基本功。7. 慢SQL优化与SQL安全从“会写”到“写得好”7.1 慢SQL排查的统一答题框架性能优化和SQL安全我在题集里放了8道属于进面试后半程才会遇到的加分题。很多候选人前面基础题答得不错到这儿就露怯了但这一部分的答题框架其实是固定的提前准备就能拿分。慢SQL排查我总结了一个四步答题框架。第一步先定位慢在哪用EXPLAIN看执行计划重点看type、key、rows三个字段——type是不是ALL全表扫描有没有命中索引预估扫描行数是多少。第二步分析是否索引失效检查WHERE条件里的列是否被函数包裹、是否有隐式类型转换、是否用了前导通配符。第三步看是否产生了不必要的临时表和排序比如ORDER BY、GROUP BY走不了索引时会在磁盘上排序慢就慢在这些地方。第四步才是改写SQL或加索引。再分享一道高频题“一张千万级的订单表怎么优化分页查询”。常规写法是LIMIT 500000, 20数据库要扫50万行再丢50万行非常慢。优化方案叫延迟关联-- 延迟关联先走索引查出目标主键再与原表关联取出完整行 SELECT o.* FROM orders o INNER JOIN ( SELECT id FROM orders ORDER BY id LIMIT 500000, 20 ) t ON o.id t.id;这个方案的核心思路是让索引覆盖扫描尽可能少的行再回表取数据。面试官让你解释“为什么这样快”你只要回答“子查询走覆盖索引避免了对全行的排序和回表”就能得分。7.2 索引失效的六种常见场景性能优化题里面试官特别喜欢问“哪些情况会导致索引失效”。这题背诵成本低但确实是排查慢SQL的第一直觉来源。我整理了六个最常考的场景建议直接背。一是对索引列使用函数比如WHERE DATE(order_date) 2024-03-01改成WHERE order_date 2024-03-01 AND order_date 2024-03-02。二是隐式类型转换比如索引列是字符串却用数字去比较数据库会隐式转类型导致索引失效。三是前导模糊匹配LIKE %关键字走不了索引LIKE 关键字%可以走。四是OR条件WHERE a 1 OR b 2如果a有索引而b没有可能全表扫描可以改成UNION ALL拆成两个查询。五是联合索引不满足最左前缀联合索引(a, b, c)只查b或c时用不上。六是统计信息不更新数据量变化大但统计信息没刷新优化器选错执行计划。另外大数据SQL面试里会延伸问到“数据倾斜”的处理。比如GROUP BY某个字段其中“未知渠道”占了90%的数据量导致单Reduce任务拖慢整个作业。常见思路包括过滤异常数据、给热点键加随机前缀打散、小表广播等。这类题在Hive/Spark SQL面试中几乎必考和我上面讲的慢SQL优化是一脉相承的。7.3 SQL注入与参数化数据安全这道题不能丢分SQL安全在数据分析岗位面试里出现的频率比以前高了。现在数据权限、数据安全越来越受重视面试官会问“你在写报表SQL时如何防止SQL注入”。我建议这样回答SQL注入的本质是拼接SQL字符串时用户输入被当成了可执行代码。比如登录场景里攻击者输入特定字符串绕过身份验证。而参数化查询的原理是让SQL语句的结构和数据分离通过占位符传递参数数据库会把用户输入当作纯数据来处理从根源上阻断注入。-- 参数化查询示意 SELECT * FROM users WHERE username ? AND password ?即使不写Java、Python代码只写报表SQL也要养成一个习惯永远不要相信外部传入的字符串能参数化就参数化尽量避免用拼接字符串的方式动态生成SQL。这个意识在数据分析团队里越来越值钱因为公司对数据安全的重视程度直接决定了这类问题会不会出现在面试里。写在最后我把这套73题维护到现在最大的感受是SQL面试题从来不缺难题缺的是把基础题答扎实的人。很多人一上来就刷窗口函数、刷复杂业务SQL结果连续登录题写出来了反而在WHERE里用别名这种基础细节上翻车。数据分析岗位的SQL面试考察的是你长期写数、取数、清洗数据的习惯这些习惯不是靠考前突击能练出来的。我自己的做法是每隔三个月就把这套题重新过一遍不是为了面试而是为了自查。如果你也在准备跳槽或者刚入行想系统补一遍SQL功底建议先按我开头说的三轮刷题法过一遍重点看文章中提到的每一道“翻车题”把这些细节刻进肌肉记忆。这套题我也会持续更新后面遇到新的高频题会继续补充到对应模块里。
返回列表