SQLPathLab
SQL 实战指南 · MySQL 8.0

每个对象的最新一行:DISTINCT 不能替代排序

用分区窗口明确“每个客户最新订单”的行级选择规则。

问题与判断方法

DISTINCT 只能删除所有所选字段都相同的重复行。只要同一客户的金额或时间不同,多条订单仍会保留,因此它不能表达“每个客户只取最新一条”。减少 SELECT 字段来迎合 DISTINCT 还可能丢失真正需要的数据。

ROW_NUMBER 可以按 customer_id 分区,再按 created_at DESC 排序,为每个客户的最新记录标记 rn = 1。时间相同时应增加 id DESC 等唯一键,保证选择结果可重复。

另一种做法是先求每个客户的 MAX(created_at) 再连接原表,但并列时间可能重新产生多行。选择方案前要明确是否允许并列、需要哪些完整字段,以及相应复合索引的列顺序。

示例

示例数据表

表名:orders
orders
customer_idamountcreated_at
101 1200 2026-01-03
102 900 2026-01-08
101 1500 2026-02-02
104 700 2026-02-15

优化前 / 不推荐

SELECT DISTINCT customer_id, amount, created_at FROM orders ORDER BY customer_id, created_at DESC;

优化前运行结果

result
customer_idamountcreated_at
101 1500 2026-02-02
101 1200 2026-01-03
102 900 2026-01-08
104 700 2026-02-15

优化后 / 推荐

WITH ranked AS (SELECT customer_id, amount, created_at, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at DESC, id DESC) AS rn FROM orders) SELECT customer_id, amount, created_at FROM ranked WHERE rn = 1 ORDER BY customer_id;

优化后运行结果

result
customer_idamountcreated_at
101 1500 2026-02-02
102 900 2026-01-08
104 700 2026-02-15

为什么这样更好:DISTINCT 仍保留 customer 101 的两条不同订单;ROW_NUMBER 按时间与 id 明确只取最新一条。

这篇内容是否清楚?