必须先用窗口函数标记每用户首单(row_number() over (partition by user_id order by created_at, id)),再过滤rn=1,最后按channel汇总sum(amount);直接group by channel会混入老客复购订单。

用 GROUP BY + SUM() 统计各渠道新客首单金额的核心逻辑
直接结论:不能对原始订单表 orders 简单 GROUP BY channel 后 SUM(amount)——那统计的是所有订单,不是「新客的首单」。必须先识别出每个用户的第一笔订单,再按渠道聚合。
关键在于两层筛选:第一层是「谁是新客」(即该用户在系统中首次下单),第二层是「这笔单是否为其首单」(时间最早的一笔)。多数人卡在第一步就误用了 MIN(created_at) 而没绑定到具体订单行。
- 错误写法:
SELECT channel, SUM(amount) FROM orders GROUP BY channel—— 混淆了新客、老客、复购 - 正确路径:先用窗口函数标出每用户的首单(
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at)),再过滤rn = 1,最后按channel汇总 - 注意:
user_id必须能唯一标识自然人;若只有设备 ID 或手机号未去重,结果会失真
用 ROW_NUMBER() 精确标记每个用户首单(MySQL 8.0+/PostgreSQL/SQL Server)
这是最通用、语义最清晰的做法。窗口函数确保「按用户分组 → 按时间排序 → 编号」三步原子执行,避免自连接或子查询的性能陷阱。
SELECT channel, SUM(amount) AS first_order_amount_total
FROM (
SELECT
channel,
amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) AS rn
FROM orders
) t
WHERE rn = 1
GROUP BY channel;
-
PARTITION BY user_id是分组依据,不是GROUP BY;它只影响窗口内排序范围 - 若存在同一秒多笔订单(如批量导入),仅靠
created_at可能无法稳定确定“第一单”,建议补上id作为次序键:ORDER BY created_at, id - MySQL 5.7 及更早版本不支持窗口函数,需改用关联子查询(性能差,数据量大时慎用)
兼容 MySQL 5.7 的替代方案:关联最小时间戳子查询
当环境受限无法升级,且数据量在百万级以内时,可用子查询先求出每个用户的最早下单时间,再关联原表取对应订单。但要注意:若同一用户在最早时间有多单,会重复计入。
SELECT o.channel, SUM(o.amount) AS first_order_amount_total FROM orders o INNER JOIN ( SELECT user_id, MIN(created_at) AS first_time FROM orders GROUP BY user_id ) t ON o.user_id = t.user_id AND o.created_at = t.first_time GROUP BY o.channel;
- 该写法隐含假设:同一用户不会在完全相同时间生成多笔首单。如有,需加
AND o.id = (SELECT MIN(id) ...)二次限定,但嵌套更深、性能更差 - 务必为
(user_id, created_at)建联合索引,否则子查询GROUP BY和主表JOIN都会变慢 - PostgreSQL 用户请优先用窗口函数——子查询方案在这里反而可能因计划器优化不足而更慢
容易被忽略的业务细节:新客定义与渠道归属一致性
技术能跑通,不代表结果可信。两个常被跳过的点,直接影响统计口径:
- 「新客」是否排除测试账号、内部员工?需提前在
WHERE中过滤user_id NOT IN (...)或关联用户维表判断is_real_user = true - 「渠道」字段来源是否可靠?例如:订单表里的
channel是下单时埋点上报,还是根据用户首次来源回填?若后者,必须从用户维度拉取(而非订单表直取),否则老客复购也会被记为原渠道 - 时间范围要明确:是统计「所有历史新客的首单」,还是「近30天内成为新客的人的首单」?后者需在最外层或子查询中加
WHERE created_at >= '2024-05-01',但注意——新客判定时间(首次下单)和统计时间(首单发生)必须统一
没有统一的新客定义和渠道归因逻辑,再准的 SQL 也只是在精确地算错东西。











