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

Laravel队列任务中复用$monthlyInvoices查询结果异常是什么原因?

问题根本原因

核心是Laravel查询构造器为可变对象,你没有克隆初始的查询构造器实例,每次追加查询条件都会直接修改原$monthlyInvoices对象的查询规则,导致后续统计叠加了前面的过滤条件,出现逻辑冲突。

原代码错误逻辑拆解

你在循环内定义的$monthlyInvoices始终是同一个查询构造器实例,实际执行顺序如下:

  1. 初始查询条件:仅按年月过滤,匹配当月所有发票
  2. 计算$total_sales:基于初始条件统计,结果正常
  3. 计算$offline_sales:给原构造器追加where('type','offline')条件,此时原$monthlyInvoices已经自带线下类型过滤规则
  4. 计算$online_sales:在已有type=offline的条件基础上,再追加where('type','online'),最终查询条件变成type既要等于offline又要等于online,自然没有匹配数据,返回结果为空。

这完全对应你观察到的现象:调换online和offline的统计顺序后,后执行的统计会叠加前一个的类型过滤条件,导致无结果;你重新定义$monthlyInvoices相当于重置了查询构造器的条件,所以功能恢复。

修复方案

推荐两种写法,第一种逻辑直观,第二种性能更优:

方案1:每次统计克隆基础查询构造器

foreach ($period as $date) {
    // 仅定义一次基础查询
    $baseQuery = Invoice::whereRaw('YEAR(created_at) = ?', $date->format('Y'))
        ->whereRaw('MONTH(created_at) = ?', $date->format('m'));

    // 每次统计都克隆基础查询,避免修改原对象
    $total_sales = (clone $baseQuery)->sum('net_price');
    $offline_sales = (clone $baseQuery)->where('type', '=', 'offline')->sum('net_price');
    $online_sales = (clone $baseQuery)->where('type', '=', 'online')->sum('net_price');

    InvoiceMonthlyStatistics::create([
        'total_sales' => $total_sales,
        'online_sales' => $online_sales,
        'offline_sales' => $offline_sales,
        'month' => $date->format('m'),
        'year' => $date->format('y')
    ]);
}

方案2:单次分组查询统计所有数据

仅查询一次数据库即可拿到所有统计结果,性能更高:

foreach ($period as $date) {
    $stats = Invoice::whereRaw('YEAR(created_at) = ?', $date->format('Y'))
        ->whereRaw('MONTH(created_at) = ?', $date->format('m'))
        ->selectRaw('type, sum(net_price) as sale_amount')
        ->groupBy('type')
        ->pluck('sale_amount', 'type');
    
    InvoiceMonthlyStatistics::create([
        'total_sales' => $stats->sum(),
        'online_sales' => $stats->get('online', 0),
        'offline_sales' => $stats->get('offline', 0),
        'month' => $date->format('m'),
        'year' => $date->format('y')
    ]);
}

额外说明:Laravel的create方法本身会直接写入数据库,你原代码循环外的$invoiceStats->save()是多余操作,只会保存最后一次循环的统计数据,没有实际作用可以直接删除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 19:36:01