千锋教育-做有情怀、有良心、有品质的职业教育机构

手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

当前位置:首页  >  应聘面试  >  软件测试面试题  > 软件测试面试常见SQL查询题详解

软件测试面试常见SQL查询题详解

来源:千锋教育
发布人:cli
时间: 2026-05-06 09:02:32 1778029352

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%的面试者。错在“没成绩的学生”的理解。

大部分人写:

sql
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,查出来的数据永远缺一块。

标准答案:

sql
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无法直接使用聚合函数。

正确写法:

sql
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%面试者混淆的点。

第三道变形题: 查询每门课程最高分的学生姓名和分数。

思路:先找每门课最高分,再与学生表关联。

sql
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及其下单次数,并按次数降序排列。

这个场景在电商测试中天天见。先写错误写法:

sql
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。

标准答案:

sql
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。

解答:

sql
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,再取反。

sql
SELECT id, name
FROM Users
WHERE id NOT IN (
    SELECT DISTINCT user_id FROM Orders
);

也可以left join + is null。但用分组聚合更巧:

sql
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)。

传统解法:自连接或子查询,复杂且效率低。窗口函数一行搞定。

sql
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窗口函数,求出每个用户与前一次登录的天数差。

sql
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)。

场景:测试电商报表准确性。

sql
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配合倒序:

sql
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中。例如:

sql
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升序。

答案:

sql
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,是逻辑:把业务问题翻译成数据语言的能力。

tags:
声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。
10年以上业内强师集结,手把手带你蜕变精英
请您保持通讯畅通,专属学习老师24小时内将与您1V1沟通
免费领取
今日已有369人领取成功
刘同学 138****2860 刚刚成功领取
王同学 131****2015 刚刚成功领取
张同学 133****4652 刚刚成功领取
李同学 135****8607 刚刚成功领取
杨同学 132****5667 刚刚成功领取
岳同学 134****6652 刚刚成功领取
梁同学 157****2950 刚刚成功领取
刘同学 189****1015 刚刚成功领取
张同学 155****4678 刚刚成功领取
邹同学 139****2907 刚刚成功领取
董同学 138****2867 刚刚成功领取
周同学 136****3602 刚刚成功领取
相关推荐HOT