如何在Laravel中用单条查询同时获取日期区间及指定日期前的金额总和
Laravel 单查询同时计算两个时间区间数值总和的实现方案
你原有代码的核心问题是Laravel的leftJoin方法仅支持传入一个关联闭包,不能通过传入多个闭包的方式实现多区间求和,这里提供两种可直接落地的实现方案:
方案1:条件聚合(推荐,性能最优)
通过CASE WHEN做条件判断,仅需要关联一次明细表,无需多次子查询,执行效率更高:
use Illuminate\Support\Facades\DB; // 可灵活替换为变量传参 $startDate = '2021-08-01'; $endDate = '2021-08-31'; $categories = Account::leftJoin('regularvoucherentrydetails', 'regularvoucherentrydetails.account_name', '=', 'accounts.id') ->select( 'accounts.id', 'accounts.name', 'accounts.seqnumber', 'accounts.accounttype', 'regularvoucherentrydetails.debitcredit', // 统计指定日期区间内的金额总和 DB::raw("SUM(CASE WHEN regularvoucherentrydetails.dates BETWEEN ? AND ? THEN regularvoucherentrydetails.amount ELSE 0 END) as salesrange"), // 统计指定起始日期之前的金额总和 DB::raw("SUM(CASE WHEN regularvoucherentrydetails.dates < ? THEN regularvoucherentrydetails.amount ELSE 0 END) as salesyesterday") ) ->groupBy( 'accounts.id', 'accounts.name', 'accounts.seqnumber', 'accounts.accounttype', 'regularvoucherentrydetails.debitcredit' ) ->orderBy('accounts.fullcode', 'asc') ->setBindings([$startDate, $endDate, $startDate]) ->get();
如果你不需要按借贷方向分开统计,把regularvoucherentrydetails.debitcredit从select和groupBy字段中移除即可。
方案2:子查询字段(与你原生SQL思路完全对齐)
如果要完全匹配你最初写的原生SQL子查询逻辑,可以用Laravel的selectSub能力实现:
use Illuminate\Support\Facades\DB; $startDate = '2021-08-01'; $endDate = '2021-08-31'; // 构建区间求和子查询 $salesRangeSub = DB::table('regularvoucherentrydetails') ->selectRaw('SUM(amount)') ->whereColumn('account_name', 'accounts.id') ->whereBetween('dates', [$startDate, $endDate]); // 构建区间前置求和子查询 $salesYesterdaySub = DB::table('regularvoucherentrydetails') ->selectRaw('SUM(amount)') ->whereColumn('account_name', 'accounts.id') ->where('dates', '<', $startDate); $categories = Account::select( 'id', 'name', 'seqnumber', 'accounttype', DB::raw($salesRangeSub->toSql() . ' as salesrange'), DB::raw($salesYesterdaySub->toSql() . ' as salesyesterday') ) // 合并子查询的参数绑定,避免SQL注入风险 ->mergeBindings($salesRangeSub) ->mergeBindings($salesYesterdaySub) ->groupBy('id', 'name', 'seqnumber', 'accounttype') ->orderBy('fullcode', 'asc') ->get();
内容的提问来源于stack exchange,提问作者user16639613
相关产品推荐
相关产品推荐

