586. 订单最多的用户

难度:简单 | 题号:586 | 链接:586. 订单最多的客户 - 力扣(LeetCode) | 数据结构:SQL

关联:从不订购的用户(同用 Orders 表,一文求"从未下单",一文求"下单最多")· 分数排名(同为「分组计数 + 排名」题)· 可对照 SQL原理_03(CTE 写法)与 SQL原理_02(窗口函数)。

🎯 问题描述

描述:查找订单数量最多的客户的 customer_number。测试用例保证订单最多的客户只有一个。

表:Orders

1
2
3
4
5
6
7
8
+-----------------+----------+
| Column Name | Type |
+-----------------+----------+
| order_number | int |
| customer_number | int |
+-----------------+----------+
order_number 是该表的主键(具有唯一值的列)。
该表包含订单 ID 与下单客户 ID 的对应关系。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输入:
Orders 表:
+--------------+-----------------+
| order_number | customer_number |
+--------------+-----------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
+--------------+-----------------+
输出:
+-----------------+
| customer_number |
+-----------------+
| 3 |
+-----------------+
解释:
customer_number = 3 的客户共有 2 个订单,多于只有 1 个订单的客户 1 和客户 2。

提示:

  • 先「按客户分组计数」,再「取计数最大的那一组」——本质是分组 Top-1。
  • 题目只保证「唯一」,但追问要求能处理并列第一的情况(见思路 2)。
  • 返回结果只有一列 customer_number。

💻 解题思路

思路1:GROUP BY + ORDER BY … LIMIT 1(用户解法)

核心逻辑

第一步按 customer_number 分组,COUNT(*) 数出每人订单数;第二步按订单数降序排,LIMIT 1 取第一名。用 CTE(WITH)把这两步拆开,可读性更好。

代码实现

1
2
3
4
5
6
7
8
9
10
# Write your MySQL query statement below
WITH t1 AS (
SELECT customer_number, COUNT(*) AS n
FROM Orders
GROUP BY customer_number
)
SELECT customer_number
FROM t1
ORDER BY n DESC
LIMIT 1;

也可以省掉 CTE 一步写完:SELECT customer_number FROM Orders GROUP BY customer_number ORDER BY COUNT(*) DESC LIMIT 1;

📊 性能分析

  • 时间复杂度:O(nlog⁡n)O(n \log n)(GROUP BY 聚合后对结果排序,排序是主导项;走主键索引可直接扫描计数)。
  • 空间复杂度:O(n)O(n)(分组结果的哈希/临时表)。

思考

  • LIMIT 1 的并列陷阱:如果多个客户并列最多,LIMIT 1 只返回其中一个,且返回哪一个不确定(取决于排序稳定性)。本题靠「唯一」保证兜底才安全。
  • 评论区常见报错:聚合函数不能嵌套,MAX(COUNT(*)) 是非法的 → 要么先聚合再排序取首,要么把计数做成子查询再取 MAX。

思路2:窗口函数 DENSE_RANK(应对并列 · Follow-up)

核心逻辑

对每个客户的订单数排名,取 ranking = 1 的全部客户。并列第一时这里会全部保留,正好回答题目的追问「如果订单最多的客户有多个怎么办」。

代码实现

1
2
3
4
5
6
7
8
9
SELECT customer_number
FROM (
SELECT
customer_number,
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS ranking
FROM Orders
GROUP BY customer_number
) AS t
WHERE ranking = 1;

📊 性能分析

  • 时间复杂度:O(nlog⁡n)O(n \log n)(分组 + 窗口排序)。
  • 空间复杂度:O(n)O(n)。
  • 与思路 1 同阶,但语义更强(天然处理并列),代价是写法更重。

思考

  • RANK() / DENSE_RANK() 在并列时都会给出 1;差别在后续名次(RANK 跳号、DENSE_RANK 不跳)。本题只取第一名,二者等价;若像 分数排名 那样需要完整名次,就必须想清楚要哪一种。
  • 另一类等价写法用 HAVING COUNT(*) = (SELECT MAX(cnt) FROM …),靠子查询取最大计数——可读但重复聚合一次,不如窗口函数干净。

📝 总结

「每组计数的 Top-1」模板:

  • 只需第一名 → GROUP BY 分组列 + ORDER BY COUNT(*) DESC + LIMIT 1(可包一层 CTE 提升可读性);
  • 需处理并列 / 完整名次 → 子查询里 DENSE_RANK() OVER (ORDER BY COUNT(*) DESC),外层筛 ranking = 1。

核心坑点:① 聚合函数不能嵌套(MAX(COUNT(*)) 非法);② LIMIT 1 在并列时结果不确定,仅适合作题目的「唯一」保证。


🔗 题集索引:LeetCode题集索引 · 算法地图:学习-数据结构与算法MOC
📎 同类题:从不订购的用户 · 分数排名 · 部门工资最高的员工 | 概念:SQL原理_02 · SQL原理_03