如何用Diesel 2.x ORM实现分组查询最新数据行?
在Diesel 2.x中实现按组查询最新数据行
常见的SQL需求是查询每组的最新数据行,比如下面的SQL可以获取每个用户的最新余额:
SELECT user, date, balance FROM balances t1 INNER JOIN ( SELECT user, max(date) as most_recent_date FROM balances GROUP BY user ) t2 ON t1.user=t2.user AND t1.date=t2.most_recent_date
方案1:模拟原SQL的关联子查询
你可以通过给子查询字段添加别名,让Diesel能够识别并关联子查询与原表。步骤如下:
- 构造子查询并给聚合字段起别名
- 使用
inner_join关联原表与子查询,明确关联条件
示例代码:
use diesel::prelude::*; use diesel::dsl::max; use diesel::alias; // 给子查询定义别名,方便后续引用字段 let t2 = alias!(t2); // 构造子查询:获取每个用户的最新日期 let latest_dates = balances::table .group_by(balances::user) .select(( balances::user.as(t2.user), max(balances::date).as(t2.most_recent_date), )); // 关联原表与子查询,筛选出最新的余额记录 let latest_balances = balances::table .inner_join(latest_dates.on( balances::user.eq(t2.user) .and(balances::date.eq(t2.most_recent_date)) )) .select((balances::user, balances::date, balances::balance)) .load::<(i32, NaiveDateTime, f64)>(&conn)?;
方案2:使用窗口函数(更简洁高效)
另一种更优雅的方式是利用窗口函数row_number(),按用户分组并按日期降序排序,取每组的第一行数据,逻辑上和原SQL完全一致:
use diesel::prelude::*; use diesel::dsl::{row_number, over, partition_by, desc}; // 给每条记录添加分组内的排名,日期越新排名越靠前 let ranked_records = balances::table .select(( balances::user, balances::date, balances::balance, row_number() .over(partition_by(balances::user).order_by(desc(balances::date))) .alias("rn"), )) .into_boxed(); // 筛选出每组排名第一的记录(即最新数据) let latest_balances = ranked_records .filter(ranked_records.field("rn").eq(1)) .select((balances::user, balances::date, balances::balance)) .load::<YourBalanceStruct>(&conn)?;
关键说明
你之前遇到的问题是因为子查询的字段没有明确的别名映射,Diesel需要清晰的字段标识才能在关联时匹配原表字段。上面两种方案都不需要编写原生SQL,完全基于Diesel的DSL实现,且功能与目标SQL一致。
内容的提问来源于stack exchange,提问作者Mike Altonji
相关产品推荐
相关产品推荐

