LeetCode高频SQL50题总结

整体总结

SQL 在数据库内部的逻辑处理顺序是这样的:

**FROM :**确定数据来源(表、子查询、视图)。

**JOIN / ON :**如果有连接,先根据 ON 条件生成笛卡尔积,再过滤掉不符合条件的行。

**WHERE :**过滤行(对原始表数据过滤,还没分组)。

**GROUP BY :**按指定字段进行分组,开始"形成小集合"。

**HAVING :**对分组后的结果再进行过滤(和 WHERE 的区别是它作用于分组之后)。

**SELECT :**选择要返回的列,执行列表达式、聚合函数。

DISTINCT :(如果有)去重。

**ORDER BY :**对结果排序。

**LIMIT / OFFSET :**返回最终结果的行数限制。

1.条件过滤(WHERE子句)

范围查询BETWEEN...AND...(闭区间)

sql 复制代码
SELECT * FROM students WHERE age BETWEEN 18 AND 25;

**集合查询:**IN

sql 复制代码
SELECT * FROM students WHERE university IN ('北京大学', '浙江大学'); 

模糊查询:(LIKE和LIKE CONCAT)

LIKE直接写死匹配模板,固定字符串+通配符

sql 复制代码
SELECT * FROM users WHERE name LIKE '史%'

CONCAT + LIKE是一个字符串拼接函数,可以避免SQL注入风险。

sql 复制代码
SELECT * FROM users WHERE city LIKE CONCAT('%', @keyword, '%');

2.聚合函数(GROUP BY & HAVING)

聚合函数:COUNT() SUM() AVG() MAX() MIN()等,使用例子如下:

聚合函数用于计算具体值,GROUP BY用于判断根据哪个键进行分组,HAVING用于过滤不符合条件的 (与WHERE的区别是,WHERE过滤的是不符合条件的

sql 复制代码
SELECT
    s.product_id,
    p.product_name
FROM Sales s
JOIN Product p ON s.product_id = p.product_id
GROUP BY p.product_id, p.product_name
HAVING 
    MIN(s.sale_date) >= '2019-01-01'
    AND MAX(s.sale_date) <= '2019-03-31'

3.多表查询(JOIN与UNION)

  • **内连接(INNER JOIN / JOIN):**返回两表交集数据。

  • **左连接(LEFT JOIN):**保留左表所有记录,以及右表匹配的数据,无匹配则补NULL。

  • **右连接(RIGHT JOIN):**保留右表所有记录 ,以及左表匹配的数据,无匹配则补NULL。

  • **全连接(LEFT JOIN + RIGHT JOIN + UNION):**保留两个表的完整数据。

  • UNION与UNION ALL用于合并两个表的数据,区别是UNION会去重,而UNION ALL 不会。

4.窗口函数 (Window Functions)

常用于处理"排名"、"连续出现"、"前 N 名"类问题。

格式: 函数名() OVER (PARTITION BY 分组列 ORDER BY 排序列)

  • ROW_NUMBER(): 依次排序,即使数值相同排名也不同 (1, 2, 3, 4)。

  • RANK(): 数值相同排名相同,但会跳号 (1, 2, 2, 4)。

  • DENSE_RANK() : 数值相同排名相同,且不跳号 (1, 2, 2, 3)。常用于"部门工资前三高"。

  • LEAD() / LAG(): 获取当前行之后/之前的某行数据。LAG(num, 1) OVER (ORDER BY id) AS prev1。

5.结果排序与限制(ORDER &LIMIT)

可设置降序或者升序

sql 复制代码
SELECT * FROM students ORDER BY gpa DESC, age ASC; 

可通过OFFSET跳过前几行数据,如果没有10行会返回空

sql 复制代码
SELECT * FROM students LIMIT 5 OFFSET 10;

6.常用函数

  • CASE WHEN ... THEN ... ELSE ... END: SQL 中的 If-Else。

  • IFNULL(val, default): 如果值为 NULL 则返回默认值。

  • COALESCE(v1, v2, ...): 返回第一个非空值。

  • ROUND(xx,2) : 保留几位小数,四舍五入,FLOOR(xx,2)下取整,CEIL上取整

7. 时间与字符串处理

  • CHAR_LENGTH(content) > 15:计算字符串长度

  • DATE_FORMAT(trans_date, "%Y-%m"):将时间转化为指定格式

  • DATEDIFF(d1, d2): 计算日期差。

  • LEFT(str, len) / SUBSTRING(): 截取字符串。

  • REGEXP: 正则表达式匹配。

LeetCode 高频50 SQL

简单讲解了以下LeetCode高频50SQL的逻辑,部分包含特殊语句或者复杂逻辑的代码已经给出,剩余简单的题目只给出了解题思路,适合代码复习。

一、 查询 (Basic Queries)

1. 可回收且低脂的产品

  • 题目描述:找出所有既是低脂(low_fats = 'Y')又是可回收(recyclable = 'Y')的产品 ID。

  • 解题思路 :使用 WHERE 子句同时过滤两个布尔/字符条件,基础语法可直接跳过。

2. 寻找用户推荐人

  • 题目描述:找出所有推荐人 ID(referee_id)不等于 2 的顾客姓名。

  • 解题思路 :注意处理 NULL 值。在 SQL 中,!= 2 不包含 NULL,必须显式判定。

  • 代码WHERE referee_id != 2 OR referee_id IS NULL

3. 大的国家

  • 题目描述:找出面积(area)至少为 300 万或人口(population)至少为 2500 万的国家。

  • 解题思路 :使用 OR 连接两个面积和人口的过滤条件。

4. 文章浏览 I

  • 题目描述:找出所有至少浏览过一次自己文章的作者 ID。

  • 解题思路:当作者 ID 等于读者 ID 时,表示自读。需去重并按 ID 升序。

  • 特殊语句DISTINCT, ORDER BY id ASC

5. 无效的推文

  • 题目描述:找出内容长度严格大于 15 个字符的推文 ID。

  • 解题思路 :筛选内容长度大于 15 的记录,CHAR_LENGTH() (获取字符数)。

  • 代码:SELECT tweet_id FROM tweets WHERE CHAR_LENGTH(content) > 15


二、 连接 (Basic Joins)

6. 使用唯一标识码替换员工 ID

  • 题目描述:展示每位用户的唯一标识码(unique_id);如果没有,则展示 null。

  • 解题思路:以员工表为主表,左外连接(LEFT JOIN)唯一码表,防止丢失没有唯一码的员工。

  • 代码LEFT JOIN ... ON

7. 产品销售分析 I

  • 题目描述:获取销售表(Sales)中所有订单对应的产品名称(product_name)、年份和价格。

  • 解题思路:内连接销售表和产品表,获取产品名称。

8. 进店却未进行过交易的顾客

  • 题目描述:找出那些进店浏览了但没有进行任何交易的顾客 ID 及其进店次数。

  • 解题思路 :左连接交易表后,通过 WHERE transaction_id IS NULL 找到未交易记录并计数。

  • 特殊语句IS NULL

9. 上升的温度

  • 题目描述:找出与其前一天相比温度更高的所有日期 ID。

  • 解题思路 :自连接同一个表,比较"今天"和"昨天"的温度,使用日期函数处理时间差,DATEDIFF(date1, date2) = 1

  • 代码

    sql 复制代码
    SELECT w1.id
    FROM Weather as w1 JOIN Weather as w2
    ON DATEDIFF(w1.recordDate, w2.recordDate) = 1
    WHERE w1.temperature > w2.temperature;

10. 每台机器的进程平均运行时间

  • 题目描述:计算每台机器完成一个进程所需的平均耗时(End 时间 - Start 时间)。

  • 解题思路 :计算每个进程的 (end - start),再对机器 ID 进行分组求平均。

11. 员工奖金

  • 题目描述:查询奖金少于 1000 的员工姓名及其奖金数额。

  • 解题思路 :左连接奖金表,筛选奖金小于 1000 或奖金为 NULL 的记录。

12. 学生们参加各科测试的次数

  • 题目描述:统计每个学生参加每一门科目测试的次数,即使没参加过也要显示 0。

  • 解题思路 :使用 CROSS JOIN 生成学生和科目的全组合,再 LEFT JOIN 考试记录进行计数。

  • 特殊语句CROSS JOIN

13. 至少有 5 名直接下属的经理

  • 题目描述:找出至少有五个直接下属的经理姓名。

  • 解题思路 :先对 managerId 分组统计数量,通过 HAVING 过滤后连接查询名字。

  • 特殊语句GROUP BY ... HAVING COUNT(*) >= 5

14. 确认率

  • 题目描述:计算每个用户的"确认率"(确认消息数 / 总请求数)。

  • 解题思路 :左连接确认表,利用 AVG(IF(action='confirmed', 1, 0)) 计算比例。

  • 特殊语句ROUND(..., 2), IFNULL(..., 0)


三、 聚合函数 (Aggregate Functions)

15. 有趣的电影

  • 题目描述:找出 ID 为奇数且描述不是 "boring" 的电影,按评分降序排列。

  • 解题思路:WHERE过滤 ID 奇偶性和描述内容,按评分降序。

16. 平均售价

  • 题目描述:计算每种产品在对应日期范围内的平均售价。

  • 解题思路 :连接销售表和价格表(条件包含日期区间),SUM(价格*数量)/SUM(数量)

17. 项目员工 I

  • 题目描述:计算每个项目下员工的平均工作年限。

  • 解题思路:按项目 ID 分组计算平均值。

18. 各赛事的用户注册率

  • 题目描述:计算每个赛事中注册用户的百分比。

  • 解题思路:按赛事分组计数,除以用户表总数。

19. 查询结果的质量和占比

  • 题目描述:计算每个查询名的质量(评分/位置的均值)和劣质查询占比(评分 < 3 的百分比)。

  • 解题思路:使用聚合函数直接计算均值和比例。

20. 每月交易 I

  • 题目描述:按月和国家统计交易数、批准数、总金额和批准金额。

  • 解题思路:按月份(格式化日期)和国家分组统计。

  • 特殊语句DATE_FORMAT(trans_date, '%Y-%m')

21. 即时食物配送 II

  • 题目描述:找所有用户的"首次订单"中,即时订单(下单日期=配送日期)所占的百分比。

  • 解题思路:子查询找到每个用户的首单日期,再判断首单是否为即时单。

  • 特殊语句(customer_id, order_date) IN (SELECT ... MIN(order_date) ...)

22. 游戏玩法分析 IV

  • 题目描述:计算首日登录后,第二天紧接着也登录的玩家比例。

  • 解题思路:找到每个玩家的首登日期,左连接表看"首登后一天"是否有记录。

  • 特殊语句DATE_ADD(min_date, INTERVAL 1 DAY)

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    WITH  First_login AS(
        SELECT player_id,
        DATE_ADD(MIN(event_date), INTERVAL 1 DAY) AS next_day
        FROM Activity
        GROUP BY player_id
    ),
    ConsecutivePlayers AS (
        SELECT DISTINCT a.player_id
        FROM Activity a
        JOIN First_login f
        ON a.player_id = f.player_id AND a.event_date = f.next_day
    )
    SELECT 
        IFNULL(
            ROUND( 
                (SELECT COUNT(*) FROM ConsecutivePlayers) /
                (SELECT COUNT(DISTINCT player_id) FROM Activity)
                , 2)
        , 0) as fraction;

四、 排序和分组 (Sorting and Grouping)

23. 每位教师所教授的科目种类的数量

  • 题目描述:统计每位老师教授了多少种不同的科目。

  • 解题思路 :按老师 ID 分组,对科目 ID 去重计数。COUNT(DISTINCT subject_id)

24. 查询近 30 天活跃用户数

  • 题目描述:统计截至 2019-07-27(含)前 30 天内的每日活跃用户数。

  • 解题思路:过滤日期范围,分组统计去重后的用户。

  • 代码:

    sql 复制代码
    SELECT
        activity_date AS day,
        COUNT(DISTINCT user_id) AS active_users
    FROM Activity 
    WHERE activity_date BETWEEN DATE_SUB('2019-07-27', INTERVAL 29 DAY) AND '2019-07-27'
    GROUP BY activity_date;

25. 销售分析 III

  • 题目描述:找出仅在 2019 年第一季度出售过的产品。

  • 解题思路 :分组后,HAVING 判定最小和最大销售日期均在指定范围内。

26. 超过 5 名学生的课

  • 题目描述:列出所有至少有 5 名学生的课程。

  • 解题思路 :分组并使用 HAVING 过滤计数。

27. 求关注者的数量

  • 题目描述:统计每个被关注者的关注者总数。

  • 解题思路:按被关注者分组统计。

28. 只出现一次的最大数字

  • 题目描述:找出只出现过一次的数字中最大的那个。

  • 解题思路 :子查询找出 COUNT = 1 的数字,外部嵌套 MAX()

  • 代码:

    sql 复制代码
    SELECT MAX(num) AS num
    FROM(
        SELECT num 
        FROM MyNumbers
        GROUP BY num 
        HAVING COUNT(*) = 1
    ) T;

29. 买下所有产品的客户

  • 题目描述:找出购买了产品表中所有种类产品的客户。

  • 解题思路:按顾客分组,统计其购买的不同产品数是否等于产品表总行数。

  • 代码:

    sql 复制代码
    SELECT customer_id
    FROM Customer 
    GROUP BY customer_id
    HAVING COUNT(DISTINCT product_key) = (SELECT COUNT(*) FROM Product);

五、 高级查询和连接 (Advanced Select and Joins)

30. 每位经理的下属员工数量

  • 题目描述:统计经理的下属人数及下属的平均年龄。

  • 解题思路:自连接。

  • 代码:

    sql 复制代码
    SELECT
        e.employee_id,
        e.name,
        COUNT(*) AS reports_count,
        ROUND(AVG(t.age)) AS average_age
    FROM Employees e 
    JOIN Employees t
    ON e.employee_id = t.reports_to
    GROUP BY e.employee_id
    ORDER BY e.employee_id;

31. 员工的直属部门

  • 题目描述:找出员工的直属部门。若只属于一个部门则选该部门,若属于多个则选标记为 'Y' 的。

  • 解题思路 :使用 UNION 结合单部门员工和标记为 Y 的员工。

32. 判断三角形

  • 题目描述:给定三条边长,判断是否能组成三角形。

  • 解题思路:判定任意两边之和大于第三边。

  • 特殊语句CASE WHEN x+y>z AND x+z>y AND y+z>x THEN 'Yes' ELSE 'No' END

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT
        x, y, z,
        CASE 
            WHEN x + y > z AND x + z > y AND y + z > x THEN 'Yes'
            ELSE 'No'
        END AS triangle
    FROM
        Triangle;

33. 连续出现的数字

  • 题目描述:找出连续出现至少三次的数字。

  • 解题思路 :判断某个数字是否在连续的 ID 中重复。LEAD()LAG()

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT
        DISTINCT num AS ConsecutiveNums
    FROM 
        (SELECT
            num,
            LAG(num, 1) OVER (ORDER BY id) AS prev1,
            LAG(num, 2) OVER (ORDER BY id) AS prev2
        FROM LOGS) T
    WHERE num = prev1 AND num = prev2;

34. 指定日期的产品价格

  • 题目描述:找出所有产品在 2019-08-16 时的价格(无调价记录则默认为 10)。

  • 解题思路:取日期前的最后一次调价记录,再并集未调价的产品。

  • 特殊语句RANK() OVER(PARTITION BY product_id ORDER BY change_date DESC)

35. 最后一个能进入巴士的人

  • 题目描述:按重量限制(1000kg)和上车顺序,找出最后一个能上车的人。

  • 解题思路 :按顺序累加重量,SUM(weight) OVER(ORDER BY turn)

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT person_name
    FROM (
        SELECT person_name,
        SUM(weight) OVER(ORDER BY turn) AS total_weight
        FROM Queue
    ) T
    WHERE total_weight <= 1000
    ORDER BY total_weight DESC
    LIMIT 1;

36. 按分类统计薪水

  • 题目描述:统计 Low Salary, Average Salary, High Salary 三个等级的账户数量。

  • 解题思路 :分别计算三个分类的数量并用 UNION 合并。


六、 子查询 (Subqueries)

37. 上级经理已离职的公司员工

  • 题目描述:找出薪水低于 3000 且其上级经理已不在公司(员工表)的员工。

  • 解题思路 :使用 NOT IN 子查询。

38. 换座位

  • 题目描述:交换相邻两个学生的座位。

  • 解题思路:奇数 ID + 1,偶数 ID - 1,考虑最后一个奇数 ID 不变。

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT (
        CASE
            WHEN MOD(id, 2) = 1 AND id = (SELECT COUNT(*) FROM Seat) THEN id
            WHEN MOD(id, 2) = 1 THEN id + 1
            ELSE id - 1
        END) AS id,
        student
    FROM Seat
    ORDER BY id;

39. 电影评分

  • 题目描述:找出评论电影最多的用户名,以及 2020 年 2 月平均分最高的电影名。

  • 解题思路 :分两部分查询并用 UNION ALL

40. 餐馆营业额变化增长

  • 题目描述:计算每一天及其前 6 天(共 7 天)的营业额总和及平均值。

  • 解题思路 :利用窗口函数定义滚动窗口范围。ROWS 6 PRECEDING

  • 代码:

    sql 复制代码
    WITH daily_amount AS (
        SELECT
            visited_on,
            SUM(amount) AS amount
        FROM Customer
        GROUP BY visited_on
    ),
    window_metrics AS (
        SELECT
            visited_on,
            SUM(amount) OVER (ORDER BY visited_on ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS amount,
            ROUND(AVG(amount) OVER (ORDER BY visited_on ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), 2) AS average_amount,
            ROW_NUMBER() OVER(ORDER BY visited_on) AS rn
        FROM daily_amount
        ORDER BY visited_on
    )
    SELECT
        visited_on,
        amount,
        average_amount
    FROM window_metrics
    WHERE rn >= 7

41. 好友申请 II :谁有最多的好友

  • 题目描述:找出拥有好友数量最多的人。

  • 解题思路:统计 ID 在申请方和接受方出现的总数。

42. 2016 年的投资

  • 题目描述:统计满足条件的投资总额:1. 2015年投资额相同;2. 经纬度位置唯一。

  • 解题思路:窗口函数计算出tiv_count和loc_count然后用where过滤袋掉不符合条件的

43. 部门工资前三高的所有员工

  • 题目描述:找出每个部门中工资排名前三的员工。

  • 解题思路 :使用密集排名,DENSE_RANK(),不跳过

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below nvb
    WITH employee_rank AS (
    SELECT
        d.name AS Department,
        e.name AS Employee,
        e.salary AS salary,
        DENSE_RANK() OVER(PARTITION BY e.departmentId ORDER BY e.salary DESC) AS sale_rank
    FROM Employee e 
    JOIN Department d ON e.departmentId = d.id
    ) 
    SELECT
        Department,
        Employee,
        salary
    FROM employee_rank
    WHERE sale_rank <= 3;

七、 高级字符串函数 / 正则表达式 / 子句 (String & Regex)

44. 修复表中的名字

  • 题目描述:将名字修复为首字母大写,其余小写。

  • 解题思路 :字符串截取并转换大小写后拼接。CONCAT, UPPER, LOWER, SUBSTR

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT user_id,
    CONCAT (
        UPPER(LEFT(name, 1)),
        LOWER(SUBSTR(name, 2))
    ) AS name 
    FROM Users
    ORDER BY user_id;

45. 患某种疾病的患者

  • 题目描述:找出患有以 "DIAB1" 开头的疾病的患者。

  • 解题思路:注意 "DIAB1" 可能是字符串开头,也可能是某个单词的开头。

  • 特殊语句LIKE 'DIAB1%' OR LIKE '% DIAB1%'

46. 删除重复的电子邮箱

  • 题目描述:删除重复的邮箱记录,仅保留 ID 最小的那一条。

  • 解题思路:自连接删除 ID 较大的重复项。

  • 代码

    sql 复制代码
    DELETE p1 
    FROM Person p1
    JOIN Person p2 ON p1.email = p2.email
    WHERE p1.id > p2.id;

47. 第二高的薪水

  • 题目描述:找出第二高的薪水,如果没有则返回 null。

  • 解题思路 :排序、去重并偏移取值,LIMIT 1 OFFSET 1

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT (
        SELECT DISTINCT salary
        FROM Employee
        ORDER BY salary DESC
        LIMIT 1 OFFSET 1
    ) AS SecondHighestSalary;

48. 按日期分组销售产品

  • 题目描述:找出每个日期销售的产品种类数及其名称(按字典序排列并逗号分隔)。

  • 解题思路 :分组并进行组内字符串聚合,GROUP_CONCAT

  • 代码:

    sql 复制代码
    # Write your MySQL query statement below
    SELECT
        sell_date,
        COUNT(DISTINCT product) AS num_sold,
        GROUP_CONCAT(DISTINCT product ORDER BY product SEPARATOR ',') AS products
    FROM Activities
    GROUP BY sell_date
    ORDER BY sell_date;

49. 列出指定时间段内所有的下单产品

  • 题目描述:找出 2020 年 2 月下单总量不少于 100 的产品名称。

  • 解题思路:日期过滤,分组求和。

50. 查找拥有有效邮箱的用户

  • 题目描述:找出邮箱前缀以字母开头,后缀为 @leetcode.com 的有效邮箱。

  • 解题思路:使用正则表达式匹配复杂模式。

  • 代码:

    sql 复制代码
    SELECT
        user_id,
        name,
        mail
    FROM Users
    WHERE REGEXP_LIKE(mail, '^[a-zA-Z][a-zA-Z0-9_.-]*@leetcode\\.com$', 'c');
相关推荐
万邦科技Lafite2 小时前
淘宝开放API获取订单信息教程(2025年最新版)
java·开发语言·数据库·人工智能·python·开放api·电商开放平台
CoderCodingNo2 小时前
【GESP】C++五级真题(前缀和思想考点) luogu-P10719 [GESP202406 五级] 黑白格
开发语言·c++·算法
zore_c2 小时前
【C语言】排序算法——希尔排序以及插入排序 ——详解!!!
c语言·数据结构·c++·笔记·算法·排序算法·推荐算法
C雨后彩虹2 小时前
ConcurrentHashMap 扩容机制:高并发下的安全扩容实现
java·数据结构·哈希算法·集合·hashmap
胡闹542 小时前
MyBatis-Plus 更新字段为 null 为何失效?
java·数据库·mybatis
Chip Design2 小时前
量子–经典混合计算生态:量子启发式、量子模拟、经典算法
算法·量子计算
BB学长2 小时前
Icepak|01功能介绍
算法·数学建模·能源·微信公众平台
G皮T2 小时前
【Elasticsearch】查询性能调优(二):SQL LIMIT 和 terminate_after 对比
大数据·sql·elasticsearch·搜索引擎·全文检索·es·opensearch
Cathy Bryant2 小时前
傅里叶变换(二):旋转楼梯
笔记·算法·数学建模·信息与通信·傅里叶分析