精选19道SQL面试题:覆盖查询、概念与常见陷阱
1. 找出第二高的薪水。
经典老题。毕竟,谁的梦想不是当第二名呢?(手动狗头)
SELECT MAX(salary)
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);
2. 找出表中的重复记录。
重复记录就像你的前任。你巴不得他们消失,但他们总阴魂不散。
SELECT column_name, COUNT(*)
FROM table_name
GROUP BY column_name
HAVING COUNT(*) > 1;
3. 找出从未提交过报告的员工。
可以称之为 SQL 版的“绩效考核”或“末位淘汰提醒器”。
SELECT e.id, e.name
FROM employees e
LEFT JOIN reports r ON e.id = r.emp_id
WHERE r.emp_id IS NULL; -- 如果在 reports 表中没有记录,则 r.emp_id 为 NULL
4. 找出第 N 高的薪水。
又名:“这查询能把我脑浆子都榨干。” / “这题谁爱做谁做!”
-- 假设 N 是你想找的排名,比如 N=3 就是第三高
SELECT DISTINCT salary
FROM employees e1
WHERE (N - 1) = ( -- 注意:这里 N 需要替换成具体数字,例如 3-1 = 2SELECT COUNT(DISTINCT e2.salary)FROM employees e2WHERE e2.salary > e1.salary
);
-- 更好的方法通常是使用窗口函数,比如 ROW_NUMBER(), RANK(), DENSE_RANK()
5. 解释一下 CROSS JOIN
(交叉连接)。
简而言之:它就是你一不小心搞出一百亿行数据,然后被DBA追杀的方法。
SELECT * FROM A CROSS JOIN B; -- A 表的每一行与 B 表的每一行组合
6. 找出员工数超过5人的部门。
因为没什么比“超过五个”更能喊出“我们部门牛逼!”的口号了。(然而并不能)
SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5;
7. 使用 ROW_NUMBER()
找出每个部门收入最高的前3名员工。
献给那些在 SQL 世界里追求“高端操作”的人。
SELECT *
FROM (SELECT e.*, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rnFROM employees e
) ranked_employees
WHERE rn <= 3;
8. 使用自连接 (Self Join) 找出员工及其经理的配对。
SQL 版《盗梦空间》。一张表自己连接自己,能出啥岔子呢?(最好别出)
SELECT e.name AS employee, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;
9. WHERE
vs. HAVING
:搏击俱乐部特别版。
WHERE
在分组前筛选数据。HAVING
在分组后筛选聚合结果。还是一头雾水?放心,你面试官也可能一样。
10. 删除重复记录,但保留一条。
又名:“求求了,手下留情,可别把整个表给干没了啊。”
-- 假设我们根据 email 列去重,保留 id 最小的那条记录
DELETE FROM employees
WHERE id NOT IN (SELECT MIN(id) -- 找出每个 email 分组中 id 最小的那个FROM employeesGROUP BY email
);
-- 注意:这个操作有风险,请先备份数据,并在测试环境验证!
11. 将行数据转换为列数据 (Pivot / 行转列)。
面试官:“用 SQL 实现这个。”
你 (内心OS):“图啥呢?直接代码里处理不香吗?”
12. 使用 EXISTS
。
因为用 IN
显得你太菜了,不够秀。
SELECT s.name
FROM students s
WHERE EXISTS ( -- 检查是否存在SELECT 1FROM grades gWHERE g.student_id = s.id -- 相关的成绩记录
);
13. 解释一下数据库范式 (Normalization)。
哦,你是指那个在 NoSQL 横行的时代里,好像没几个人真正鸟的玩意儿?
14. 相关子查询 (Correlated Subqueries)。
你又在跟自己 JOIN 了,只不过这次换了个马甲。你还好吗,哥们?需要心理疏导不?
-- 找出薪水高于其所在部门平均薪水的员工
SELECT e.name, e.salary
FROM employees e
WHERE e.salary > (SELECT AVG(s.salary)FROM employees sWHERE s.department_id = e.department_id -- 子查询依赖外部查询的 e.department_id
);
15. 找出两张表共有的记录 (Common records between two tables)。
SQL 版的“陌陌点赞,配对成功”。
SELECT * FROM A
INTERSECT -- 交集操作
SELECT * FROM B;
16. 按部门更新薪水。
就是那个你的 UPDATE
查询一不小心就可能让公司 CFO 菊花一紧的时刻。
UPDATE employees
SET salary = salary * 1.1 -- 比如给 10 号部门涨薪 10%
WHERE department_id = 10;
-- 千万别忘了 WHERE 条件!不然就是P0级事故!
17. 找出没有下属的经理。
数据库里的社恐本恐,或者光杆司令。
SELECT e.name
FROM employees e
WHERE e.is_manager = TRUE -- 假设有个字段标记是否为经理(或者在 manager 表中)AND e.id NOT IN (SELECT DISTINCT manager_id -- 找出所有被别人作为经理ID引用的IDFROM employeesWHERE manager_id IS NOT NULL);
-- 更简单的可能是:
-- SELECT m.name FROM employees m LEFT JOIN employees e ON m.id = e.manager_id WHERE m.is_manager = TRUE AND e.id IS NULL;
18. 将行转置为列 (Transpose rows into columns)。
因为你老板觉得 SQL 就应该跟 Excel 一样万能,点几下就能出透视表。
19. 找出销量最高的前3名商品。
因为“排名 = 营收”嘛。至少老板是这么PUA我们的。
SELECT item, COUNT(*) AS freq
FROM sales
GROUP BY item
ORDER BY freq DESC
LIMIT 3;