练习leetcode上高频sql50题,主要是记录思路的分析过程
四、排序和分组
619. 只出现一次的最大数字
🔹1. 理解题目目标
我们要找出:
在表中只出现一次的那些数字中,最大的那个。
关键点:
- 「只出现一次」 ⇒ 意味着要 统计每个数字出现的次数。
- 「最大的」 ⇒ 在只出现一次的数里,取 最大值。
- 「如果不存在这样的数字」 ⇒ 返回
NULL
🔹2. 涉及的 SQL 技术
功能 | SQL 技术 |
---|---|
统计每个数字出现的次数 | GROUP BY num + COUNT(*) |
只保留出现一次的 | HAVING COUNT(*) = 1 |
找出最大值 | MAX() |
处理空值情况 | 默认 MAX() 返回 NULL 如果没结果,所以不需要特别处理 |
🔹3. 拆解步骤
Step 1:统计每个 num
出现的次数
1 | sql |
Step 2:只保留那些只出现一次的数字
1 | sql |
Step 3:在只出现一次的数字中,找出最大值
1 | sql |
✅ 最终 SQL 答案:
1 | sql |
1045. 买下所有产品的客户
🔹1. 题目目标:
我们需要找出:
那些 在 Customer 表中,购买了 Product 表中所有产品 的
customer_id
。
换句话说:
- 找出 “全集匹配” 的客户。
- 就是说:某个
customer_id
,其购买的产品集合 == Product 表中所有的产品集合
✅ 技术点分析
需要哪些 SQL 技术点?
需求 | SQL 技术 |
---|---|
产品总数 | SELECT COUNT(*) FROM Product |
每位客户买了多少不同产品 | GROUP BY customer_id + COUNT(DISTINCT product_key) |
只保留那些买的产品数 == 产品总数的客户 | HAVING COUNT(DISTINCT product_key) = 产品总数 |
✅ 分步骤构造思路
🔸 第一步:统计 Product 表中总共有多少种产品
1 | SELECT COUNT(*) FROM Product; |
例如,如果结果是 3 种产品,那么我们就要找出那些买了 3 种不同产品 的客户。
🔸 第二步:统计每位客户买了多少种不同产品
注意这里要用DISTINCT,因为可能一个用户重复买了某个产品多次
1 | SELECT customer_id, COUNT(DISTINCT product_key) AS product_count |
🔸 第三步:只保留那些 product_count = 产品总数 的客户
我们需要把产品总数用子查询或 CTE 引入进来。
✅ 最终 SQL 答案(推荐写法)
1 | SELECT customer_id |
✅ 解题小结
步骤 | 说明 |
---|---|
1️⃣ | 用 GROUP BY 分组每个客户 |
2️⃣ | 用 COUNT(DISTINCT product_key) 统计他买了多少种产品 |
3️⃣ | 用 HAVING 只保留买的产品数 == 总产品数的客户 |
4️⃣ | 子查询 SELECT COUNT(*) FROM Product 用来获取总产品种类 |
五、高级查询和连接
1731. 每位经理的下属员工数量
这是一个很经典的自连接(self join)分析题。我们一起来逐步分析它的思路和该怎么写 SQL。
✅ 题目目标总结
我们要找出:
所有有下属的经理(即:别人的
reports_to
指向他),并返回:
employee_id
(经理ID)name
(经理名字)- 有多少人向他汇报(直接下属数量)
- 这些下属的平均年龄(四舍五入)
并按 employee_id
升序排序。
✅ 步骤分析
🔹1. 明确谁是“经理”
我们要找的经理,是那些 在别人 reports_to 字段中被提及过的员工,即:
1 | SELECT DISTINCT reports_to FROM Employees WHERE reports_to IS NOT NULL |
也就是被别人汇报的员工。
🔹2. 自连接:员工汇报给谁?
我们需要将员工表自连接,将汇报者和他们的经理连接起来:
1 | SELECT |
这样可以找出:谁向谁汇报,以及下属的年龄。
🔹3. 聚合:按经理分组统计下属人数和平均年龄
在上面的结果基础上:
COUNT(*)
统计每个经理的直接下属人数AVG(e.age)
求下属的平均年龄- 用
ROUND()
函数对平均年龄四舍五入
✅ 最终 SQL 语句如下:
1 | SELECT |
1789. 员工的直属部门
✅ 一、如何分析这道题的思路?
🔍 表示什么?
- 一个员工(
employee_id
)可能在多个部门(department_id
) - 但他必须有一个直属部门
- 如果
primary_flag = 'Y'
→ 说明明确标出了哪个是直属 - 如果他只有一个部门,即使
primary_flag = 'N'
,这个部门仍视为直属部门
- 如果
✅ 所以要做两件事:
① 找出所有 primary_flag = 'Y'
的员工部门记录(明确标记的)
② 再找出那些只在一个部门的员工(即 COUNT(department_id) = 1
),他们即使标的是 'N'
,也认定是直属部门
最后,把这两部分数据合并(UNION
)
❌ 二、你写的 SQL 为什么错了?
你写的两个 SQL 语法上都有结构性错误。我们看一下你写的:
1 | SELECT |
❌ 错误1:GROUP BY
必须在 WHERE
之后
SQL 执行顺序中,WHERE
是用来过滤原始数据行的,而 GROUP BY
是在过滤后分组
你不能在 WHERE
中使用聚合函数 COUNT()
,因为它在 GROUP BY
之后才生效。
应该写成:
1 | GROUP BY ... |
❌ 错误2:||
是不合法的 SQL 运算符
SQL 中布尔 “或” 是 OR
,不是 ||
所以这句也错了:
1 | WHERE count(...) = 1 || primary_flag = 'Y'; |
❌ 错误3:逻辑错误
即使语法都对了,把 COUNT(department_id) = 1 OR primary_flag = 'Y'
写在一起也不合理,因为:
COUNT()
是分组聚合函数(整体层面)primary_flag
是行级属性(行层面)
这两个逻辑不能混用在一个 HAVING
里。
✅ 三、正确写法(推荐用 UNION)
1 | -- 第一部分:明确标记直属的 |