在HAVING子句中使用MIN()查询客户最早订单日期遇异常的原因及解决
为什么GROUP BY + HAVING的写法无法返回每个用户的最早订单日期?
问题原因
你原SQL的核心问题出在GROUP BY的逻辑和非聚合列的处理上:
- 当你用
GROUP BY customer_id时,数据库会把同一个customer_id的行合并成一个分组。此时SELECT里的order_date属于非聚合、非分组键的列,在大多数严格的SQL模式(比如MySQL的ONLY_FULL_GROUP_BY开启时)会直接报错;如果模式宽松,数据库会随机从该分组的行里选一个order_date返回,这个值不一定是最小的那个。 - 接着
HAVING MIN(order_date) = order_date的判断,只有当随机选中的这个order_date刚好等于该分组的最小日期时,这条记录才会被保留。像你提到的customer_id=7,分组后随机返回的是'2019-07-22',而该分组的MIN(order_date)是'2019-07-08',条件不满足,所以这条记录就被过滤掉了。
简单说:GROUP BY后你拿到的order_date是随机的,不是全部分组内的日期,自然无法和最小日期匹配出所有符合条件的行。
可行的解决方法
方法1:你提到的子查询+WHERE IN
这种写法先通过子查询算出每个用户的最小订单日期,再用WHERE条件匹配原表中对应的行:
SELECT customer_id, order_date FROM Delivery WHERE (customer_id, order_date) IN ( SELECT customer_id, MIN(order_date) FROM Delivery GROUP BY customer_id )
方法2:窗口函数(更高效的写法)
用ROW_NUMBER()窗口函数给每个用户的订单按日期排序,标记出最早的那一行,再筛选出来:
SELECT customer_id, order_date FROM ( SELECT customer_id, order_date, ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY order_date ASC) AS rn FROM Delivery ) t WHERE rn = 1
如果存在同一个用户同一天多个订单的情况,想保留所有的话,可以把ROW_NUMBER()换成RANK()或者DENSE_RANK()。
内容的提问来源于stack exchange,提问作者BenS
相关产品推荐
相关产品推荐

