
本教程旨在指导用户如何使用Laravel Query Builder执行包含多表联接、复杂聚合函数及条件筛选的SQL查询,并正确地将结果展示在Blade视图中。文章将详细分析原始SQL与Query Builder的转换要点,纠正常见的语法错误,特别是`select`、`DB::raw()`和`havingRaw`的正确使用,并提供有效的调试策略,以解决如“Undefined variable”等问题。
Laravel的查询构造器(Query Builder)提供了一种便捷、流式接口来构建和执行数据库查询。它抽象了底层数据库的差异,使开发者能够用PHP代码而非原生SQL来操作数据库。然而,当面对包含多表联接(JOIN)、复杂聚合函数(SUM, ROUND)、分组(GROUP BY)以及对聚合结果进行筛选(HAVING)的复杂查询时,将原生SQL语句准确无误地转换为Query Builder语法可能会遇到挑战。本教程将通过一个实际案例,深入探讨如何克服这些挑战。
在开始使用Laravel Query Builder之前,我们首先需要彻底理解原始的SQL查询语句。这有助于我们明确查询的目标、涉及的表、联接条件、选择的列、聚合逻辑以及筛选规则。
以下是需要转换的原始SQL查询:
SELECT ru.external_ref_no AS SID, usd.user_name AS Username, rs.servicecode AS Package, rc.clientdesc as Entity, rc.clientip as NAS_IP, ROUND((ROUND((SUM(usd.FREE_UPLOAD_OCTETS)/1048576)))/1024,2) AS Upload, ROUND((ROUND((SUM(usd.FREE_DOWNLOAD_OCTETS)/1048576)))/1024,2) AS Download, ROUND((ROUND((SUM(usd.FREE_UPLOAD_OCTETS)/1048576)))/1024,2) + ROUND((ROUND((SUM(usd.FREE_DOWNLOAD_OCTETS)/1048576)))/1024,2) AS Total_Usage FROM user_session_detail usd, radservice rs, radclient rc, radgroup rg, raduser ru WHERE ru.username=usd.user_name AND rs.serviceid=usd.service_id AND rg.groupid=usd.group_id AND usd.client_id=rc.clientid AND usd.SESSION_START_TIME > '2021-09-30 00.00.01' AND usd.SESSION_START_TIME < '2021-09-30 23.59.59' GROUP BY usd.user_name HAVING (ROUND((SUM(usd.FREE_UPLOAD_OCTETS)/1048576)))/1024 + (ROUND((SUM(usd.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 > 15 AND (ROUND((SUM(usd.FREE_UPLOAD_OCTETS)/1048576)))/1024 + (ROUND((SUM(usd.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 < 20;
核心要素分析:
在将上述原生SQL转换为Laravel Query Builder时,可能会遇到以下常见问题:
Query Builder的 select 方法可以接受字符串、数组或 DB::raw() 表达式。当需要选择原始SQL表达式(如聚合函数)时,必须使用 DB::raw()。如果同时需要选择特定表的全部列和自定义表达式,应将它们合并到同一个 select 调用中,或者合理链式调用。
错误示例 (常见误区):
// 试图将所有列和DB::raw表达式分开,可能导致只选择最后一个select的内容
->select(array('user_session_detail.*'))
->select(DB::raw('raduser.external_ref_no AS SID, ...'))
// 或将DB::raw放在select数组中,但如果DB::raw内容过长,可读性差
->select(['user_session_detail.*', DB::raw('...')]) 正确方式:
将所有需要选择的列和 DB::raw 表达式作为参数传递给单个 select 方法,或者使用 addSelect 方法追加选择。
->select('user_session_detail.*', DB::raw('raduser.external_ref_no AS SID, ...'))
// 或者
->select('user_session_detail.user_name', 'raduser.external_ref_no AS SID') // 选择特定列
->addSelect(DB::raw('ROUND((SUM(...))) AS Upload')) // 追加聚合列错误示例 (将复杂表达式直接传入 having 或 havingRaw 参数类型错误):
// 错误:having 方法不接受复杂的DB::raw表达式作为第一个参数
->having(DB::raw('(ROUND((SUM(...)))/1024) > 15'))
// 错误:havingRaw的参数必须是字符串,而不是PHP表达式
->havingRaw((ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024 + (ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 > 15) 正确方式:
将完整的 HAVING 条件作为字符串传递给 havingRaw()。
->havingRaw('(ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024 + (ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 > 15 AND (ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 + (ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/10448576)))/1024 < 20')这是最常见的错误之一,也是导致查询失败的隐蔽原因。例如,将 user_session_detail 拼写为 user_session_detai。这类错误会导致数据库无法找到指定的表或列,进而抛出SQL语法错误或查询结果为空,最终可能导致Blade视图中出现“Undefined variable”错误,因为查询未能成功执行并返回数据。
错误示例:
->join('radservice', 'user_session_detai.service_id', '=', 'radservice.serviceid') // 注意 'user_session_detai' 拼写错误正确方式:
仔细核对所有表名和列名,确保与数据库中的定义完全一致。
->join('radservice', 'user_session_detail.service_id', '=', 'radservice.serviceid')结合上述分析和纠正,以下是实现原始SQL查询的完整Laravel Query Builder代码:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
class ReportController extends Controller
{
public function nsuresecret()
{
$user_session_detail = DB::table('user_session_detail')
// 首先进行所有联接操作
->join('radservice', 'user_session_detail.service_id', '=', 'radservice.serviceid')
->join('radclient', 'user_session_detail.client_id', '=', 'radclient.clientid')
->join('radgroup', 'user_session_detail.group_id', '=', 'radgroup.groupid')
->join('raduser', 'user_session_detail.user_name', '=', 'raduser.username')
// 筛选时间范围
->whereBetween('user_session_detail.SESSION_START_TIME', ['2021-09-30 00:00:01', '2021-09-30 23:59:59'])
// 根据用户名分组
->groupBy('user_session_detail.user_name')
// 选择需要的列,包括原始列和聚合计算列
->select(
'user_session_detail.user_name', // 明确选择基础列,避免歧义
DB::raw('
raduser.external_ref_no AS SID,
user_session_detail.user_name AS Username,
radservice.servicecode AS Package,
radclient.clientdesc as Entity,
radclient.clientip as NAS_IP,
ROUND((ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024,2) AS Upload,
ROUND((ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024,2) AS Download,
ROUND((ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024,2) + ROUND((ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024,2) AS Total_Usage
')
)
// 对聚合结果进行筛选
->havingRaw('
(ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024 +
(ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 > 15
AND
(ROUND((SUM(user_session_detail.FREE_UPLOAD_OCTETS)/1048576)))/1024 +
(ROUND((SUM(user_session_detail.FREE_DOWNLOAD_OCTETS)/1048576)))/1024 < 20
')
->get(); // 执行查询并获取结果集
// 调试:在将数据传递给视图之前,检查数据是否正确
// dd($user_session_detail);
return view('reports.secretuserlist', compact('user_session_detail'));
}
}代码解释:
一旦控制器成功获取并传递了数据,Blade视图就可以轻松地迭代并展示这些数据。
<!-- resources/views/reports/secretuserlist.blade.php -->
<table class="table table table-bordered table-striped table-hover">
<thead>
<tr>
<th>SID</th>
<th>Username</th>
<th>Package</th>
<th>Entity</th>
<th>NAS_IP</th>
<th>Upload (GB)</th>
<th>Download (GB)</th>
<th>Total Usage (GB)</th>
</tr>
</thead>
<tbody>
@forelse($user_session_detail as $usd)
<tr>
<td>{{ $usd->SID }}</td>
<td>{{ $usd->Username }}</td>
<td>{{ $usd->Package }}</td>
<td>{{ $usd->Entity }}</td>
<td>{{ $usd->NAS_IP }}</td>
<td>{{ $usd->Upload }}</td>
<td>{{ $usd->Download }}</td>
<td>{{ $usd->Total_Usage }}</td>
</tr>
@empty
<tr>
<td colspan="8">暂无数据</td>
</tr>
@endforelse
</tbody>
</table>视图代码解释:
当遇到“Undefined variable”或其他查询相关问题时,有效的调试至关重要。
$query = DB::table('user_session_detail')
// ... (your query chain)
->toSql();
dd($query);这可以帮助你检查生成的SQL是否与你期望的原始SQL一致。
$bindings = DB::table('user_session_detail')
// ... (your query chain)
->getBindings();
dd($bindings);这对于调试 where 或 having 子句中的参数问题非常有用。
以上就是Laravel Query Builder实现复杂联接、聚合查询及视图展示的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号