You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL查询错误分析:分组未按预期获取用户最早订单日期

问题分析与解答

问题背景

表结构与数据

Create table If Not Exists Delivery (delivery_id int, customer_id int, order_date date, customer_pref_delivery_date date);
Truncate table Delivery;
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('1', '1', '2019-08-01', '2019-08-02');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('2', '2', '2019-08-02', '2019-08-02');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('3', '1', '2019-08-11', '2019-08-12');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('4', '3', '2019-08-24', '2019-08-24');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('5', '3', '2019-08-21', '2019-08-22');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('6', '2', '2019-08-11', '2019-08-13');
insert into Delivery (delivery_id, customer_id, order_date, customer_pref_delivery_date) values ('7', '4', '2019-08-09', '2019-08-09');

需求与错误SQL

需求:获取每个用户的最早订单记录,最终结果按customer_id升序排列。

用户编写的错误SQL:

with t1 as (select *
            from delivery
            order by customer_id, order_date),
t2 as (select * from t1 group by customer_id)
select * from t2;

执行后发现customer_id=3的记录,order_date为2019-08-24,而非预期的2019-08-21。

错误原因解析

1. CTE中的ORDER BY不保证后续数据集的顺序

CTE(公共表表达式)本质是一个临时数据集,如果没有搭配LIMIT子句,数据库优化器会忽略CTE内部的ORDER BY。这是因为SQL是基于集合的语言,集合本身是无序的,除非在最终的查询中明确指定ORDER BY,否则数据库不会保证数据的排列顺序。所以t1的排序对后续t2的查询没有任何约束作用。

2. GROUP BY的非标准用法导致结果随机

select * from t1 group by customer_id属于不符合SQL标准的写法(仅部分数据库如MySQL在关闭ONLY_FULL_GROUP_BY参数时允许)。当使用GROUP BY分组时,SELECT子句中未被聚合的字段,数据库会随机选取该分组内的任意一行数据,完全不会遵循之前的排序逻辑。这就是为什么customer_id=3的分组里,最终取到的是order_date=2019-08-24的记录,而非更早的那条。

正确实现方式

如果要获取每个用户的最早订单记录,推荐以下两种标准写法:

方法1:使用窗口函数ROW_NUMBER()

通过窗口函数为每个用户的订单按order_date排序,标记出最早的那条:

WITH ranked_delivery AS (
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date ASC) AS rn
    FROM Delivery
)
SELECT delivery_id, customer_id, order_date, customer_pref_delivery_date
FROM ranked_delivery
WHERE rn = 1
ORDER BY customer_id;

方法2:使用聚合函数关联子查询

先通过聚合函数找到每个用户的最早订单日期,再关联原表获取完整记录:

SELECT d.*
FROM Delivery d
INNER JOIN (
    SELECT customer_id, MIN(order_date) AS earliest_order_date
    FROM Delivery
    GROUP BY customer_id
) t ON d.customer_id = t.customer_id AND d.order_date = t.earliest_order_date
ORDER BY d.customer_id;

内容的提问来源于stack exchange,提问作者ZEESHAN AHMED

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:37:49