
在laravel应用开发中,开发者可能会遇到一个令人困惑的性能问题:一个包含leftjoin和paginate的查询在laravel中执行缓慢(例如17秒),但将其生成的sql语句直接复制到数据库管理工具(如phpmyadmin)中执行时,却能迅速完成(例如0.2秒)。这种性能差异通常指向laravel查询构建器中对sql语句的生成方式存在某种误解或不当使用。
以下是一个典型的慢查询示例:
$sql = DB::table('inventorysku_tb as isku')
->leftJoin('inventorytrackingmodules_tb as itm', function ($join) {
$join->where('itm.coID', '=', 4)
->whereBetween('itm.effectiveDate', ['2021-05-01', '2021-05-31'])
->on('itm.skuID', '=', 'isku.ID');
});
// 此时执行 paginate 会非常慢
$results = $sql->paginate(25);通过toSql()方法检查上述Laravel代码生成的SQL语句,并手动替换问号占位符后,得到的SQL可能如下:
select *
from `inventorysku_tb` as `isku`
left join `inventorytrackingmodules_tb` as `itm`
on `itm`.`skuID` = `isku`.`ID`
and `itm`.`coID` = 4
and `itm`.`effectiveDate` between '2021-05-01' and '2021-05-31'尽管生成的SQL看起来与在phpMyAdmin中快速执行的SQL一致,但Laravel的内部处理机制,尤其是在leftJoin的闭包中混合使用where和on时,可能会导致意想不到的行为。一个关键的观察是,如果将leftJoin改为innerJoin,查询速度会显著提升,这进一步暗示了问题可能出在leftJoin条件下where子句的处理上。
在Laravel的查询构建器中,join方法的闭包内部,on和where方法虽然都能用于添加条件,但它们的语义和最终生成的SQL语句在某些情况下存在微妙但关键的区别。
根据Laravel官方文档,on子句可以链式调用,例如:
$join->on('contacts.user_id', '=', 'users.id')
->on('contacts.info_id', '=', 'info.id');
// 这将生成:on contacts.user_id = users.id and contacts.info_id = info.id这明确指出,所有连接条件都应通过on方法来构建。
解决上述性能问题的关键在于确保所有连接条件都通过on方法明确地定义在ON子句中。对于涉及字面量值或范围的条件,如whereBetween,也需要将其封装在on方法内部的闭包中。
以下是优化后的Laravel查询代码:
$sql = DB::table('inventorysku_tb as isku')
->leftJoin('inventorytrackingmodules_tb as itm', function ($join) {
// 使用 on 定义所有连接条件
$join->on('itm.coID', '=', DB::raw(4)) // 确保字面量值也被正确处理
->on('itm.skuID', '=', 'isku.ID')
->on(function ($query) {
// 将 whereBetween 封装在 on 的闭包中
$query->whereBetween('itm.effectiveDate', ['2021-05-01', '2021-05-31']);
});
});
// 此时执行 paginate 应该会显著加快
$results = $sql->paginate(25);代码解释:
通过这种方式,Laravel查询构建器将生成与phpMyAdmin中快速执行的SQL语句完全一致的ON子句,从而允许数据库优化器有效利用相关索引,显著提升查询性能。
$sqlQuery = $sql->toSql(); $bindings = $sql->getBindings(); dd($sqlQuery, $bindings);
然后手动将绑定值代入SQL,并在数据库管理工具中执行,以确认其性能。
Laravel的查询构建器是一个强大且灵活的工具,但它要求开发者对SQL底层原理和Laravel的内部实现有清晰的理解。在处理leftJoin操作时,尤其需要注意on与where方法的正确使用。通过将所有连接条件,包括涉及字面量值和范围的条件,都通过on方法(或其闭包形式)来定义,我们可以确保Laravel生成高效且符合预期的SQL语句,从而避免潜在的性能陷阱,尤其是在结合paginate使用时。始终验证生成的SQL是诊断和解决此类性能问题的最佳实践。
以上就是优化Laravel查询:深入理解Left Join中where与on的性能陷阱的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号