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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:57:18