01 为什么SQL是测试面试的必考题
面试官问一道SQL题,10个测试员9个答错,剩下1个写了半分钟改了三遍还是错的。
SQL查询能力不是测试岗位的加分项,是筛选门槛。数据库里躺着千万条数据,不会查数据的测试,连Bug都定位不准。
不会SQL的测试,就像不会游泳的救生员。
看看市场数据:某招聘平台统计,2023年软件测试岗位面试题中,SQL题出现率92%。其中80%的面试者会在多表连接或分组聚合上丢分。为什么?因为大多数测试员只懂“select * from table”,遇到业务逻辑就懵了。
软件测试的核心工作之一:验证数据是否正确。
前端展示10个商品,后台库存是不是10?用户下单后,订单状态是否更新?这些都需要SQL去查。如果面试官问“如何查出重复注册的用户”,你连group by都不会,直接淘汰。
SQL面试题考的不是死记硬背,是逻辑拆解能力。
一道题往往映射真实业务场景:
- 查询未支付订单 → 考察where条件过滤
- 统计每个部门最高工资 → 考察子查询与聚合
- 找出连续3天登录的用户 → 考察窗口函数
这些题不超纲,但80%的人写不出正确语句。原因很简单:练习太少,只会复制粘贴,不会独立思考。
给你一组数字:
某培训机构统计,经系统训练后,面试SQL题通过率从12%提升到78%。差距不在智商,在方法。
掌握了常见题型和解题套路,任何SQL题都能拆成已知模块。
本模块核心:
SQL在测试面试中的地位不可替代。不是要背答案,是要学会“读题→拆解→写代码”的链路。下个模块开始,直接上真实面试题,告诉你标准答案和踩坑点。
---
02 必考题型:多表连接与子查询
面试题: 有两张表:学生表(Student)和成绩表(Score)。Student有字段id, name,Score有字段student_id, course, score。
要求:查出所有学生(包括没成绩的学生)的姓名及其数学成绩,如果没有数学成绩则显示0。
这道题砍倒70%的面试者。错在“没成绩的学生”的理解。
大部分人写:
SELECT s.name, sc.score
FROM Student s, Score sc
WHERE s.id = sc.student_id AND sc.course = '数学';问题:inner join会过滤掉没选数学的学生,结果只有选了数学的人。要求“所有学生”,必须用左连接left join。
不会用left join,查出来的数据永远缺一块。
标准答案:
SELECT s.name, COALESCE(sc.score, 0) AS math_score
FROM Student s
LEFT JOIN Score sc ON s.id = sc.student_id AND sc.course = '数学';关键点:
1. LEFT JOIN保留左表所有行。
2. join条件里加AND sc.course = '数学',不是放在where里。why?放在where会过滤掉null行,把没数学成绩的人又筛掉了。
第二道变形题: 查出每个学生所有科目的平均分,并显示平均分≥80的学生姓名和均分。
典型错误:先用where过滤平均分≥80,再分组。但where无法直接使用聚合函数。
正确写法:
SELECT s.name, AVG(sc.score) AS avg_score
FROM Student s
JOIN Score sc ON s.id = sc.student_id
GROUP BY s.id, s.name
HAVING AVG(sc.score) >= 80;必须用HAVING,不能用WHERE。
WHERE过滤原始行,HAVING过滤分组后结果。这是80%面试者混淆的点。
第三道变形题: 查询每门课程最高分的学生姓名和分数。
思路:先找每门课最高分,再与学生表关联。
SELECT sc.course, s.name, sc.score
FROM Score sc
JOIN Student s ON sc.student_id = s.id
WHERE (sc.course, sc.score) IN (
SELECT course, MAX(score)
FROM Score
GROUP BY course
);子查询里分组取最大值,外层用IN匹配。也可以使用窗口函数(下个模块讲)。
这道题考的是子查询和in的灵活运用。
小结:
多表连接必考,left join和join的区别,where与having的区别,子查询的嵌套。记住:读题先确认是否要保留所有记录,再决定连接类型。
---
03 必考题型:分组聚合与HAVING
面试题: 有一张订单表Orders,字段:order_id, user_id, amount, create_date。要求:查出2023年下单次数超过5次的用户ID及其下单次数,并按次数降序排列。
这个场景在电商测试中天天见。先写错误写法:
SELECT user_id, COUNT(*) AS cnt
FROM Orders
WHERE cnt > 5
GROUP BY user_id
ORDER BY cnt DESC;错得离谱:where不能引用聚合列,且顺序写反。正确顺序:from → where → group by → having → order by。
标准答案:
SELECT user_id, COUNT(*) AS cnt
FROM Orders
WHERE create_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY user_id
HAVING COUNT(*) > 5
ORDER BY cnt DESC;注意:COUNT(*)统计行数,包括null。但order_id通常不为空,没问题。如果要统计订单量,建议COUNT(order_id)。
顺序写错,面试官直接画叉。
第二道题: 查看每个用户消费总金额,并筛选出总金额超过1000的用户,同时显示他们的平均消费金额。
要求输出user_id, total_amount, avg_amount。
解答:
SELECT user_id,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount
FROM Orders
GROUP BY user_id
HAVING SUM(amount) > 1000;HAVING后面可以跟多个聚合条件。注意:WHERE过滤原始行,如某个用户有异常订单(amount<0),可以在where先排除。
第三道题: 查出没有下过单的用户(用分组聚合实现)。
用not in?可以,但分组思想:
先查出所有下过单的user_id,再取反。
SELECT id, name
FROM Users
WHERE id NOT IN (
SELECT DISTINCT user_id FROM Orders
);也可以left join + is null。但用分组聚合更巧:
SELECT u.id, u.name
FROM Users u
LEFT JOIN Orders o ON u.id = o.user_id
GROUP BY u.id, u.name
HAVING COUNT(o.order_id) = 0;左连接后,没下单的用户o.order_id为null,count结果为0。注意count(null)=0,但count(1)或count(*)会算1行,所以必须count(o.order_id)字段。
进阶:
分组聚合常与条件计数组合。例如:统计每个用户2023年支付的订单数vs未支付的订单数。
使用SUM(CASE WHEN status='paid' THEN 1 ELSE 0 END)实现。
这类题是测试中验证数据一致性的利器。
总结规律:分组聚合考三件事——1. 正确使用group by列(select中非聚合字段必须在group by中),2. having与where分工,3. 聚合函数嵌套计算。掌握这三点,攻克50%的SQL面试题。
---
04 必考题型:窗口函数与排名
面试题: 要求查询每个部门工资最高的前三名员工(员工表EMP:emp_id, name, dept_id, salary)。
传统解法:自连接或子查询,复杂且效率低。窗口函数一行搞定。
SELECT emp_id, name, dept_id, salary,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk
FROM EMP但RANK会跳过并列名次(如两个第一名之后是第三名)。如果要求前三名且包括并列,用DENSE_RANK。如果只取固定行数,用ROW_NUMBER(不保留并列)。
窗口函数是SQL面试的杀手锏,会与不会差距一个层级。
第二道题: 找出连续3天登录的用户。表Login:user_id, login_date。
测试中常验证用户留存或活跃度。常用思路:
使用LAG或LEAD窗口函数,求出每个用户与前一次登录的天数差。
WITH t AS (
SELECT user_id, login_date,
LAG(login_date, 2) OVER (PARTITION BY user_id ORDER BY login_date) AS lag2_date
FROM Login
)
SELECT DISTINCT user_id
FROM t
WHERE DATEDIFF(login_date, lag2_date) = 2;用LAG(login_date, 2)获取前2行,如果当前日期减两天前日期等于2,说明连续3天。也可以用自连接,但窗口函数性能更优。
第三道题: 计算每个用户当月累计消费金额(running total)。
场景:测试电商报表准确性。
SELECT user_id, order_date, amount,
SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS running_total
FROM Orders
ORDER BY user_id, order_date;窗口函数中ORDER BY子句定义累加顺序。注意:不加ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,默认就是到当前行。
第四道题: 查询每个用户最后一次登录的设备。
窗口函数ROW_NUMBER配合倒序:
SELECT user_id, device, login_date
FROM (
SELECT user_id, device, login_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date DESC) AS rn
FROM Login
) t
WHERE rn = 1;子查询生成排名,外层过滤第一名。这思路解决一切“每组前N”问题。
窗口函数相比传统写法,代码量减半,逻辑更清晰。面试官看到你用窗口函数,印象分立刻提升。
但注意:窗口函数在MySQL 8.0+才支持,面试时可以说“假设数据库版本支持”,避免扣分。
---
05 避坑指南与答题技巧
坑一:忘记处理NULL值。
面试题:统计每个班级男生女生人数。表Student有gender字段,可能为NULL。
如果直接COUNT(gender),NULL不计数。正确:COUNT(CASE WHEN gender='男' THEN 1 END),或SUM(CASE WHEN gender='男' THEN 1 ELSE 0 END)。
记住:聚合函数自动忽略NULL,除非用COUNT(*)。
一段SQL里,NULL能毁掉整个人生。
坑二:group by后面字段不完整。
MySQL低版本默认开启ONLY_FULL_GROUP_BY,select非聚合字段必须出现在group by中。例如:
SELECT user_id, name, COUNT(*)
FROM Orders
GROUP BY user_id;name不在group by中,除非name与user_id一一对应,否则报错或用聚合函数包裹。面试时明确写全group by字段。
坑三:连表时忘记指定表别名。
多个表有相同字段,不写别名导致歧义。比如WHERE id = ...,必须写t1.id = t2.id。
代码规范:每个表给别名,每个字段带前缀。
坑四:子查询不加别名。
FROM子句中的子查询必须加别名。如FROM (SELECT ...) t,否则报错。
坑五:HAVING中使用别名。
有些数据库(如MySQL)允许HAVING引用select中的别名,但SQL标准不允许。写HAVING时用原聚合表达式,避免歧义。
答题技巧三步法:
1. 读题拆需求:圈出“所有”“每个”“最高”“连续”等关键词,判断联结类型、分组粒度、排序列。
2. 写骨架:先写出SELECT…FROM…WHERE…GROUP BY…HAVING…ORDER BY…的框架,再填具体内容。
3. 验数据:假设少量数据手动跑一遍逻辑,确认结果符合题意。
最后压轴一道综合题:
表A记录用户信息,表B记录订单。要求:查出2024年1月消费总额前5的用户及其总金额,如果金额相同,按用户ID升序。
答案:
SELECT u.user_id, u.name, SUM(o.amount) AS total_amount
FROM Users u
LEFT JOIN Orders o ON u.user_id = o.user_id
AND o.create_date BETWEEN '2024-01-01' AND '2024-01-31'
GROUP BY u.user_id, u.name
HAVING total_amount IS NOT NULL -- 或用SUM(o.amount) > 0
ORDER BY total_amount DESC, u.user_id ASC
LIMIT 5;注意:left join保留没有订单的用户,其SUM为NULL,用HAVING过滤。排序时金额降序,ID升序。
SQL不是背出来的,是写出来的。面试前刷50道题,面试时直接秒杀。
---
总结金句
面试考的不是SQL,是逻辑:把业务问题翻译成数据语言的能力。
关键字:#单元测试 单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是...详情>>
2022-09-01 14:30:52
1、看看是不是有frame/iframe嵌套。 WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌的页面元素无法直接定位。 解决方法...详情>>
2022-09-01 14:30:36
1、去掉验证码:对于开发人员来说,只是把相关的代码注释掉,适用于测试环境,不适用于正式环境。2、设置万能验证码:不取消验证码,而是在程序...详情>>
2022-09-01 14:30:33
关键字:#自动化测试工具 #QTP/UFT UFT是一种商业自动化工具,支持各种测试环境Web,桌面,SAP,Delphi,Net,ActiveX,Flex,Java,Oracle,Mo...详情>>
2022-09-01 14:30:27
岗位:#自动化测试工程师 关键字:#自动化测试劣势 #自动化测试不足 当受测试的应用程序频繁更改时 一次测试案例 临时 - 随机测试详情>>
2022-09-01 14:30:18
京公网安备 11010802030320号