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

Laravel使用subWeek()查询近7天记录返回8天数据问题排查

enter image description here

当前日期为 2/24/2022

在报表页面中,我从查询参数读取interval值,控制器代码如下:

$inputs    = Request::all();
$interval  = 'week'; // <<------------ 默认值 

if(array_key_exists('interval', $inputs)){
    $interval  = $inputs['interval'];
}


switch ($interval) {
    case 'day':
    $q = BabyLog::where('updated_at', '>', now()->today());
    break;
    case 'week':
    $q = BabyLog::where('updated_at', '>', now()->subWeek());
    break;
    case 'month':
    $q = BabyLog::where('updated_at', '>', now()->subMonth());
    break;
    case 'year':
    $q = BabyLog::where('updated_at', '>', now()->subYear());
    break;
    default:
    $q = BabyLog::orderBy('updated_at', 'desc');
    break;
}


$logs = $q->where('babyId',$baby->id)->orderBy('updated_at', 'desc')->get()->groupBy(function ($log) {
    return $log->updated_at->format('Y-m-d');
});

执行dd($logs);返回结果如下:

Illuminate\Database\Eloquent\Collection {#344 ▼
  #items: array:8 [▼
    "2022-02-24" => Illuminate\Database\Eloquent\Collection {#352 ▶}
    "2022-02-23" => Illuminate\Database\Eloquent\Collection {#353 ▶}
    "2022-02-22" => Illuminate\Database\Eloquent\Collection {#351 ▶}
    "2022-02-21" => Illuminate\Database\Eloquent\Collection {#349 ▶}
    "2022-02-20" => Illuminate\Database\Eloquent\Collection {#350 ▶}
    "2022-02-19" => Illuminate\Database\Eloquent\Collection {#348 ▶}
    "2022-02-18" => Illuminate\Database\Eloquent\Collection {#346 ▶}
    "2022-02-17" => Illuminate\Database\Eloquent\Collection {#345 ▶}
  ]
}

我的需求是仅在图表中展示最近7天的记录,现有以下疑问:

  1. 为什么结果列表中会包含2022-02-17的数据?
  2. 上述代码存在什么错误?
  3. 调用subWeek()是否总会返回8天的数据?
  4. 是否需要对subWeek()的结果减1天来修正?

补充说明:调用subMonth()、subYear()时并未出现该问题,不需要额外做减1天处理。


问题解答

  1. 为什么结果包含2022-02-17的数据?
    你的查询条件写的是updated_at > now()->subWeek()。当前日期是2022-02-24,假设你执行代码的时间是2月24日15:30,now()->subWeek()算出来的时间就是2月17日15:30。这个条件会捞取所有2月17日15:30之后生成的记录,自然会包含2月17日15:30到24点之间的数据,按日期分组后就会出现2022-02-17的分组。

  2. 上述代码存在什么错误?
    核心问题是时间边界没有对齐自然日:你要按天分组展示最近N天的统计,却用了精确到时分秒的动态时间作为查询边界,没有把边界锚定到对应日期的0点,导致边界时间之前所属自然日的部分数据被纳入查询范围。
    另外还有个冗余问题:default分支里提前写了orderBy('updated_at', 'desc'),后面查询构造时又重复调用了一次orderBy,属于多余代码。

  3. 调用subWeek()是否总会返回8天的数据?
    不会。返回多少天的数据完全取决于你执行代码的时间点:如果你刚好在某天0点整执行这段代码,now()->subWeek()刚好落在7天前的0点,此时查询结果就只会有7个日期分组;只要执行时间晚于当天0点,就会把7天前0点到边界时刻的那部分数据查出来,凑出8个日期分组。

  4. 是否需要对subWeek()的结果减1天来修正?
    完全不需要,减1天是错误的修复方式,会导致你少查一天数据。
    正确的修复方式是把时间边界对齐到自然日的起始点:如果要展示包含当天在内的最近7天数据,把周维度的查询条件改成BabyLog::where('updated_at', '>=', now()->subDays(6)->startOfDay())即可,这个条件会捞取6天前0点到当前时刻的所有数据,刚好覆盖7个自然日,不会多查出更早日期的记录。
    你提到subMonth()、subYear()没出现这个问题只是数据巧合——刚好这两个方法算出的边界时间点之前,对应上个月/上一年的那个时间点没有产生数据而已,逻辑本身是有问题的,只要对应时间点有数据,一样会出现多一个日期分组的情况。


内容的提问来源于stack exchange,提问作者code-8

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.13 16:15:54