跳到主要内容

LeetCode SQL 50 题题解

该教程参考 LeetCode 官方精选的高频 SQL 50 题(基础版)进行整理。这些题目涵盖了从基础查询到进阶连接、子查询及窗口函数的各类核心考点,是应对数据库面试的极佳练习材料。

为什么刷 SQL 题很重要?

在实际工作中,数据查询是后端开发、数据分析、算法工程师等岗位的日常需求。无论是业务数据统计、用户行为分析,还是系统性能监控,都离不开 SQL。面试中考察 SQL,主要看以下几点:

  1. 基础语法掌握程度:能否熟练使用 SELECTWHEREGROUP BYHAVING 等基础语句
  2. 多表关联能力:理解各种 JOIN 的区别,能根据业务场景选择合适的连接方式
  3. 复杂查询思维:能否用子查询、窗口函数解决复杂的统计问题
  4. 代码规范性:可读性、性能考量、边界情况处理

SQL 知识体系概览

核心知识点

SQL 的知识体系可以分为八个核心模块,它们从基础到高级层层递进:

难度分布

难度级别题目数量核心考点
简单约 20 题基础查询、简单 JOIN、聚合函数
中等约 25 题多表连接、子查询、窗口函数
困难约 5 题复杂窗口函数、多层嵌套子查询

SQL 执行顺序

理解 SQL 的执行顺序对于编写复杂查询至关重要。与书写顺序不同,SQL 引擎按以下顺序处理查询:

这个顺序解释了为什么在 WHERE 中不能使用 SELECT 中定义的别名(因为 WHERESELECT 之前执行),也解释了为什么聚合函数的过滤必须用 HAVING 而非 WHERE(因为 HAVINGGROUP BY 之后执行)。

示例理解执行顺序

SELECT department_id, AVG(salary) AS avg_salary  -- 7. 选择列(这里可以使用别名)
FROM employees -- 1. 确定数据来源
WHERE status = 'active' -- 4. 过滤行(这里不能用 avg_salary)
GROUP BY department_id -- 5. 分组
HAVING AVG(salary) > 10000 -- 6. 分组后过滤(这里可以用聚合函数)
ORDER BY avg_salary DESC -- 9. 排序(这里可以用别名)
LIMIT 5; -- 10. 限制结果

学习路径建议

第一阶段:夯实基础

先掌握最基本的查询语法,这是所有复杂查询的基石:

  1. SELECT 语句:理解列选择、别名、去重
  2. WHERE 条件:熟练使用各种比较和逻辑运算符
  3. ORDER BY 排序:单字段、多字段排序
  4. 聚合函数COUNTSUMAVGMAXMIN

练习重点select.mdaggregate-functions.md 中的题目

第二阶段:掌握连接

JOIN 是 SQL 的灵魂,理解连接的本质是理解"表间关系":

  1. INNER JOIN:只返回两表都有匹配的行
  2. LEFT JOIN:左表全保留,右表无匹配则为 NULL
  3. 自连接:同一表自己连接自己,常用于层级数据
  4. 连接条件:理解 ON 子句的作用

关键理解LEFT JOIN ... WHERE right.id IS NULL 是找出"在左表但不在右表"的经典模式

练习重点joins.md 中的题目

第三阶段:分组与聚合

分组是数据统计的核心:

  1. GROUP BY 原理:将数据分成多个"桶",每个桶进行聚合计算
  2. HAVING vs WHERE
    • WHERE 在分组前过滤(行级过滤)
    • HAVING 在分组后过滤(组级过滤)
  3. 条件聚合SUM(IF(condition, value, 0))SUM(condition)(MySQL 特有)

练习重点sorting-and-grouping.mdaggregate-functions.md

第四阶段:子查询与窗口函数

这是从"会写 SQL"到"精通 SQL"的分水岭:

  1. 子查询位置

    • SELECT 子句:标量子查询
    • FROM 子句:派生表
    • WHERE 子句:INEXISTS、比较运算
  2. 窗口函数(面试高频):

    • ROW_NUMBER():连续编号,无并列
    • RANK():并列跳号
    • DENSE_RANK():并列不跳号
    • LAG() / LEAD():获取前后行的值

窗口函数核心语法

函数名() OVER (
PARTITION BY 分组列 -- 可选,类似 GROUP BY
ORDER BY 排序列 -- 排序规则
ROWS BETWEEN ... -- 可选,窗口范围
)

练习重点subqueries.mdadvanced-select-and-joins.md

常见易错点

1. NULL 值处理

-- 错误:使用 = 判断 NULL
WHERE bonus = NULL -- 永远不成立

-- 正确:使用 IS NULL
WHERE bonus IS NULL

-- NULL 参与运算结果也是 NULL
SELECT NULL + 1; -- 结果是 NULL

-- 使用 IFNULL 或 COALESCE 处理
SELECT IFNULL(bonus, 0);
SELECT COALESCE(phone, mobile, 'N/A');

2. WHERE vs HAVING

-- 错误:在 WHERE 中使用聚合函数
SELECT department, COUNT(*)
FROM employees
WHERE COUNT(*) > 5 -- 报错!
GROUP BY department;

-- 正确:使用 HAVING
SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;

3. JOIN 条件遗漏

-- 错误:忘记连接条件,产生笛卡尔积
SELECT * FROM orders, customers; -- 结果行数 = orders × customers

-- 正确:添加连接条件
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id;

4. 排名函数混淆

函数相同值示例结果
ROW_NUMBER()不同编号1, 2, 3, 4, 5
RANK()相同编号,跳号1, 2, 2, 4, 5
DENSE_RANK()相同编号,不跳号1, 2, 2, 3, 4

选择建议:

  • 需要唯一编号:用 ROW_NUMBER()
  • 需要真实排名(有空缺):用 RANK()
  • 需要连续排名(无空缺):用 DENSE_RANK()

学习建议

  1. 先理解再动手:每道题先思考解题思路,再写代码
  2. 多解法对比:同一道题尝试用子查询、JOIN、窗口函数等不同方式实现
  3. 关注性能:了解索引、避免全表扫描
  4. 总结归纳:按题型整理解题模板

教程目录

1. 基础查询 (Select)

  • 查询 - [1757, 584, 595, 1148, 1683]

2. 连接查询 (Joins)

  • 连接 - [1378, 1068, 1581, 197, 1661, 577, 1280, 570, 1934]

3. 聚合与分组 (Aggregate Functions)

4. 进阶查询

  • 高级查询和连接 - [1731, 1789, 610, 180, 1164, 1204, 1907]
  • 子查询 - [1978, 626, 1341, 1321, 602, 585, 601] (注:部分题目包含 部门工资前三高)

5. 窗口函数(面试高频)

6. 综合技巧


学习建议

  1. 掌握基础语法:理解 WHERE vs HAVING, JOIN 的各种类型(INNER, LEFT, RIGHT, FULL)。
  2. 窗口函数:大部分中等及以上难度的题目(如“部门工资前三高”)可以使用窗口函数 RANK(), DENSE_RANK(), ROW_NUMBER() 极大地简化逻辑。
  3. 分逻辑处理:复杂的 SQL 建议先写子查询理清中间逻辑,再进行外层组装。