Laravel Eloquent按日期分组取每日最新记录替代max()方案
问题核心
你当前使用max(amount)、max(unit)的写法只会返回分组内对应字段的最大值,无法保证取到的数值、单位属于同一条当日最新录入的记录,会出现数值和单位不匹配、返回值不是最新录入值的错误。
实现方案
方案1:窗口函数写法(推荐)
适用于MySQL 8.0+、PostgreSQL、SQLite 3.25+等支持窗口函数的数据库版本,性能最优,逻辑最简洁:
通过ROW_NUMBER()窗口函数按日期分区,按记录的录入时间倒序排序,每个日期分区取排序第一位的记录,就是当日最新录入的数据。
$records = Records::where("user",$uid) ->where("type", "biometric") ->where("fk", $bid) ->where("datetime","<" ,$range->max) ->where("datetime",">" ,$range->min) ->selectRaw(" amount as damount, unit as unit, date(datetime) as datee, ROW_NUMBER() OVER ( PARTITION BY date(datetime) ORDER BY datetime DESC, id DESC ) as rn ") ->having("rn", 1) ->orderBy("datee","desc") ->get();
排序规则里加id DESC是兜底逻辑,避免同一天出现两条datetime完全一致的记录时返回多条结果,会默认取主键更大(也就是后插入)的那条。
方案2:关联子查询写法(兼容旧版数据库)
如果使用MySQL 5.x等不支持窗口函数的版本,可以用子查询先查出每日最新记录的时间戳,再关联原表取对应记录的字段值:
// 构造每日最新记录时间的子查询 $dailyLatestSub = Records::where("user",$uid) ->where("type", "biometric") ->where("fk", $bid) ->where("datetime","<" ,$range->max) ->where("datetime",">" ,$range->min) ->selectRaw("MAX(datetime) as latest_time, DATE(datetime) as datee") ->groupBy("datee"); // 关联原表拿最新记录的数值、单位 $records = Records::joinSub($dailyLatestSub, 'daily_latest', function($join) use ($uid, $bid) { $join->on("records.datetime", "=", "daily_latest.latest_time") ->where("records.user", $uid) ->where("records.type", "biometric") ->where("records.fk", $bid); }) ->selectRaw("records.amount as damount, records.unit as unit, daily_latest.datee as datee") ->orderBy("datee", "desc") ->get();
注意:如果业务逻辑里
datee是表内自带的日期字段,不是从datetime计算得来,可以把上述代码里DATE(datetime)的部分直接替换为表字段datee,查询效率会更高。
内容的提问来源于stack exchange,提问作者Vaheed01
相关产品推荐
相关产品推荐

