Laravel9下YajraBox Datatables服务端添加合计行方法问询
解决方案:Laravel 9 + Yajra Datatables 服务端模式添加全局合计
针对百万级数据量的场景,完全可以通过 Query Builder 在数据库层面处理合计逻辑,避免集合操作或前端API的局限性。以下提供两种实用方案:
方案一:每页页脚显示全局合计(推荐)
该方案将全局合计数据附加到Datatables响应中,前端在页脚渲染,确保每页都能看到全量数据的统计结果,性能最优。
1. 后端代码调整
先封装重复的筛选条件闭包,再分别获取业务数据和合计数据:
use DB; use DataTables; // 封装筛选条件,避免重复代码 $filterCallback = function ($query) use ($finalItem) { foreach ($finalItem as $keyword) { if (is_numeric($keyword)) { $query->orWhere('login', '=', $keyword); } else { $query->orWhere('group', 'like', '%' . $keyword . '%'); } } }; // 原业务数据查询 $result = DB::table('table') ->select('column1', 'column2', 'column3', 'column4', 'column5', 'column6', 'column7', 'column8', 'column9', 'column10', 'Group', 'Login', 'time_msc') ->whereBetween('time_msc', [$fromDate, $toDate]) ->where($filterCallback) ->latest('deal'); // 获取全局合计数据 $totals = DB::table('table') ->select( DB::raw('SUM(column1) as total_column1'), DB::raw('SUM(column2) as total_column2'), DB::raw('SUM(column3) as total_column3') ) ->whereBetween('time_msc', [$fromDate, $toDate]) ->where($filterCallback) ->first(); // 将合计数据附加到Datatables响应中 return Datatables::of($result) ->with('totals', [ 'column1' => $totals->total_column1 ?? 0, 'column2' => $totals->total_column2 ?? 0, 'column3' => $totals->total_column3 ?? 0, ]) ->toJson();
2. 前端渲染逻辑
通过Datatables的drawCallback回调,从响应中取出合计数据并渲染到页脚:
$('#your-table-id').DataTable({ serverSide: true, ajax: { url: '/your-data-api-url', dataSrc: function (json) { // 缓存合计数据 window.tableTotals = json.totals; return json.data; } }, drawCallback: function () { const api = this.api(); const footer = api.table().footer(); // 将合计值渲染到对应列的页脚(索引对应column1/2/3的列位置) $(footer).find('td:eq(0)').text(`合计: ${window.tableTotals.column1}`); $(footer).find('td:eq(1)').text(window.tableTotals.column2); $(footer).find('td:eq(2)').text(window.tableTotals.column3); } });
方案二:合计行作为数据最后一行(仅最后一页显示)
如果需要将合计行作为数据的一部分展示在最后一页,可通过UNION ALL合并业务查询与合计查询:
use DB; use DataTables; $filterCallback = function ($query) use ($finalItem) { foreach ($finalItem as $keyword) { if (is_numeric($keyword)) { $query->orWhere('login', '=', $keyword); } else { $query->orWhere('group', 'like', '%' . $keyword . '%'); } } }; // 原业务数据查询,添加标识字段区分普通行与合计行 $dataQuery = DB::table('table') ->select( 'column1', 'column2', 'column3', 'column4', 'column5', 'column6', 'column7', 'column8', 'column9', 'column10', 'Group', 'Login', 'time_msc', DB::raw('0 as is_total') ) ->whereBetween('time_msc', [$fromDate, $toDate]) ->where($filterCallback) ->latest('deal'); // 合计行查询,字段数量与类型需与原查询完全匹配 $totalQuery = DB::table('table') ->select( DB::raw('SUM(column1) as column1'), DB::raw('SUM(column2) as column2'), DB::raw('SUM(column3) as column3'), DB::raw('NULL as column4'), DB::raw('NULL as column5'), DB::raw('NULL as column6'), DB::raw('NULL as column7'), DB::raw('NULL as column8'), DB::raw('NULL as column9'), DB::raw('NULL as column10'), DB::raw('\'合计\' as `Group`'), DB::raw('NULL as Login'), DB::raw('NULL as time_msc'), DB::raw('1 as is_total') ) ->whereBetween('time_msc', [$fromDate, $toDate]) ->where($filterCallback); // 合并两个查询,确保合计行在最后 $finalQuery = $dataQuery->unionAll($totalQuery)->orderBy('is_total', 'asc')->orderBy('deal', 'desc'); return Datatables::of($finalQuery)->toJson();
注意事项
- 两种方案均在数据库层面完成统计,不会将全量数据加载到内存,适合百万级数据场景。
- 使用
UNION ALL时,需保证两个查询的字段数量、顺序、数据类型完全一致,否则会抛出SQL错误。 - 合计查询与业务查询共享相同的筛选条件,确保统计结果与业务数据范围一致。
内容的提问来源于stack exchange,提问作者SWAT 10101
相关产品推荐
相关产品推荐

