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

Ecto使用group_by时如何选择字段?如何取分组最新inserted_at及替代方案

解决方案

报错原因说明

SQL 规范要求使用 group by 时,select 返回的字段要么属于分组字段,要么被聚合函数包裹,因此你直接返回未分组也未聚合的 inserted_at 会触发报错。以下是满足「不直接对inserted_at使用聚合函数、拿到每个分组最后插入数据的inserted_at」需求的可选方案,以及可替代group by的实现方式:

可选实现方案

  • 方案1:窗口函数(兼容性最高,全数据库支持)
    先按user_id、role_id分组,组内按inserted_at倒序排序,取每个组排名第一的记录即可,不需要对inserted_at做聚合处理。
    Ecto 代码示例:

    from t in "activity",
      where: t.id == ^id,
      window: [p: [partition_by: [t.user_id, t.role_id], order_by: [desc: t.inserted_at]]],
      where: row_number() |> over(:p) == 1,
      select: %{inserted_at: t.inserted_at}
    
  • 方案2:子查询关联匹配
    子查询先拿到每个分组的最大插入时间,再和原表关联匹配对应记录,最终select的inserted_at直接取自原表,无需对该字段做聚合。
    Ecto 代码示例:

    # 子查询获取每个分组的最大插入时间
    group_max_q = from t in "activity",
      where: t.id == ^id,
      group_by: [t.user_id, t.role_id],
      select: %{user_id: t.user_id, role_id: t.role_id, max_ts: max(t.inserted_at)}
    
    # 关联原表拿到对应记录的inserted_at
    from t in "activity",
      join: gm in subquery(group_max_q),
      on: t.user_id == gm.user_id and t.role_id == gm.role_id and t.inserted_at == gm.max_ts,
      select: %{inserted_at: t.inserted_at}
    
  • 方案3:distinct on语法(仅PostgreSQL数据库支持)
    可以完全替代group by实现分组取最新的效果,写法更简洁。
    Ecto 代码示例:

    from t in "activity",
      where: t.id == ^id,
      distinct: [t.user_id, t.role_id],
      order_by: [t.user_id, t.role_id, desc: t.inserted_at],
      select: %{inserted_at: t.inserted_at}
    

group by 替代方案

上面提到的窗口函数、distinct on、子查询关联三种方案,都可以完全替代group by实现按user_id、role_id分组取数的效果,不需要在主查询声明group by子句。


内容的提问来源于stack exchange,提问作者Srikant Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:57:04