Laravel多表Join查询结合Datatables加载慢优化咨询
财务数据查询与Datatables加载优化方案
针对你遇到的大数据量下查询慢、Datatables加载卡顿问题,从数据库查询、Laravel代码、前端配置三个层面给出具体优化方案:
一、数据库查询优化
1. 替换关联子查询为预聚合查询
原查询中eqivalent_sum使用关联子查询,每条financial_entries记录都会触发一次SUM计算,数据量上升后开销极大。改为提前聚合交易数据再关联,大幅减少计算次数:
// 预聚合credit类型的交易总和,仅执行一次聚合 $transactionSum = DB::table('finance_transaction') ->select('entry_id', DB::raw('ROUND(SUM(equivalent), 2) as eqivalent_sum')) ->where('type', 'credit') ->groupBy('entry_id'); // 主查询关联预聚合结果 $financial_entries = FinanceEntryMod::select([ 'ts.eqivalent_sum', 'financial_entries.id', 'financial_entries.created_by', 'financial_entries.branch_id', 'financial_entries.order_id', 'financial_entries.number', DB::raw('IFNULL(branches.name, IFNULL(v2_branches.name, "main_branch")) AS branch_name'), 'financial_entries.v2_order_id', 'financial_entries.v2_document_id', 'financial_entries.date', 'financial_entries.is_generated', 'financial_entries.note', 'financial_entries.created_at', 'financial_entries.updated_at', DB::raw('DATE_FORMAT(financial_entries.date, "%Y-%m-%d") AS new_date'), DB::raw('DATE_FORMAT(financial_entries.created_at, "%Y-%m-%d") AS new_created_at') ]) ->leftJoinSub($transactionSum, 'ts', fn($join) => $join->on('ts.entry_id', '=', 'financial_entries.id')) ->leftJoin('branches', 'branches.id', '=', 'financial_entries.branch_id') ->leftJoin('finance_transaction', 'financial_entries.id', '=', 'finance_transaction.entry_id') ->leftJoin('finance_accounts', 'finance_accounts.id', '=', 'finance_transaction.account_id') ->leftJoin('v2_branches', 'v2_branches.id', '=', 'finance_accounts.v2_branch_id') ->where('financial_entries.is_deleted', '=', '0') ->groupBy('financial_entries.id');
注:原查询Joinfinance_transaction后再分组会导致数据先膨胀再压缩,预聚合方案避免了这个问题
2. 添加核心索引
给查询涉及的关联、过滤字段添加索引,直接提升数据库查询效率:
-- 交易表:按entry_id+type聚合的组合索引 CREATE INDEX idx_finance_trans_entry_type ON finance_transaction(entry_id, type); -- 主表:过滤删除状态的索引 CREATE INDEX idx_financial_entries_is_deleted ON financial_entries(is_deleted); -- 账户表:关联v2分支的索引 CREATE INDEX idx_finance_accounts_v2_branch ON finance_accounts(v2_branch_id);
确保各表主键(id)索引存在,这是关联查询的基础
3. 精简计算与字段
- 把
DATE_FORMAT日期格式化逻辑移到前端处理,减少数据库计算压力 - 检查
select中的字段,只保留Datatables实际需要的列,去掉冗余字段
二、Laravel代码层面优化
1. 适配Datatables服务器端参数
你已开启serverSide: true,但后端必须处理前端传递的分页、排序、搜索参数,否则会返回全量数据。推荐使用yajra/laravel-datatables扩展快速适配:
// 安装扩展后,直接返回处理后的结果 return DataTables::of($financial_entries)->make(true);
如果手动处理,需获取start/length/order/search参数,在查询中添加:
$start = request()->input('start', 0); $length = request()->input('length', 30); $orderColumn = request()->input('order.0.column', 'id'); $orderDir = request()->input('order.0.dir', 'asc'); $search = request()->input('search.value', ''); $financial_entries->skip($start)->take($length) ->orderBy($orderColumn, $orderDir) ->when($search, fn($q) => $q->where('financial_entries.number', 'like', "%{$search}%"));
2. 缓存非实时数据
如果财务数据更新频率低,用Redis缓存高频查询结果:
$cacheKey = 'financial_entries_' . md5(request()->query()); $financial_entries = Cache::remember($cacheKey, 300, function() { // 这里放你的查询代码 });
缓存时间根据业务需求调整,示例为5分钟
三、前端Datatables优化
1. 精简配置
{ "pageLength": 20, // 减少单页加载数据量 "serverSide": true, "processing": true, "deferRender": true, // 延迟渲染未显示的行 "columns": [ { "data": "id", "orderable": true, "searchable": true }, { "data": "branch_name", "orderable": false, "searchable": false }, // 其他列:给不需要排序/搜索的列关闭对应功能 ] }
2. 优化前端渲染
- 用Datatables自带的
render函数处理日期、金额格式化,避免全局循环处理数据 - 关闭不必要的插件功能(如导出、打印),减少前端资源开销
内容的提问来源于stack exchange,提问作者OMAR R A SHAIKHOMAR
相关产品推荐
相关产品推荐

