如何更简便地查询每位客户首次购买的菜单商品?
你的思路方向是对的,但先提个关键问题:你当前代码里的ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY customer_id)排序字段错了!按customer_id排序根本没法区分订单的先后,应该改成ORDER BY order_date ASC,这样才能确保拿到的是客户首次购买的商品,否则如果同一个客户有多条订单,结果是随机的,不符合需求。
接下来回答你的问题:当前方案修正排序后是可行的,但确实有更简洁的实现方式,下面给你几种常用的写法:
1. 修正后的原方案(最易读)
先把排序字段改对,这是最基础的正确写法,CTE的方式逻辑清晰,适合新手理解:
WITH cte_product AS ( SELECT sales.customer_id, menu.product_name, ROW_NUMBER() OVER (PARTITION BY sales.customer_id ORDER BY sales.order_date ASC) row_a FROM sales INNER JOIN menu ON sales.product_id = menu.product_id ) SELECT customer_id, product_name FROM cte_product WHERE row_a = 1
2. 子查询替代CTE(更紧凑)
如果不想用CTE,可以把窗口函数逻辑放到子查询里,写法更紧凑:
SELECT customer_id, product_name FROM ( SELECT s.customer_id, m.product_name, ROW_NUMBER() OVER (PARTITION BY s.customer_id ORDER BY s.order_date ASC) rn FROM sales s JOIN menu m ON s.product_id = m.product_id ) t WHERE rn = 1
3. 用FIRST_VALUE窗口函数(一行搞定)
如果你的SQL方言支持FIRST_VALUE(比如MySQL 8+、PostgreSQL、SQL Server等),可以直接在主查询里拿到首次购买的商品,不需要筛选行:
SELECT DISTINCT s.customer_id, FIRST_VALUE(m.product_name) OVER ( PARTITION BY s.customer_id ORDER BY s.order_date ASC ) AS first_purchased_product FROM sales s JOIN menu m ON s.product_id = m.product_id
这里用DISTINCT是因为每个客户会有多个订单行,FIRST_VALUE会给每一行都返回同一个首次商品,去重后就得到每个客户的唯一结果。
4. 传统GROUP BY + JOIN写法(兼容旧版本SQL)
如果你的数据库不支持窗口函数(比如MySQL 5.x),可以用这种传统方法:先找到每个客户的最早订单日期,再关联回去获取对应的商品:
SELECT s.customer_id, m.product_name FROM sales s JOIN menu m ON s.product_id = m.product_id JOIN ( SELECT customer_id, MIN(order_date) AS first_order_date FROM sales GROUP BY customer_id ) t ON s.customer_id = t.customer_id AND s.order_date = t.first_order_date
注意:如果同一个客户在同一天买了多个商品,这个写法会返回所有当天购买的商品;如果要只返回其中一个,需要再加一层处理(比如取最小的product_id),而窗口函数的写法可以通过ROW_NUMBER或RANK控制。
内容的提问来源于stack exchange,提问作者Ale
相关产品推荐
相关产品推荐

