
在使用 SQL 的 GROUP BY 子句进行按月统计时,例如统计每月删除的用户总数,数据库只会对实际存在的记录进行分组。这意味着,如果某个月份没有任何符合条件的记录,该月份将不会出现在最终的查询结果中。例如,如果1月有数据,2月没有,3月有数据,那么查询结果只会显示1月和3月的数据,2月的数据则会“跳过”,这对于需要连续时间序列数据的图表展示来说,是不可接受的。
假设我们有一个 User 模型,并希望统计每月被删除的用户数量:
use App\Models\User;
use Carbon\Carbon;
$data = User::selectRaw('DATE_FORMAT(deleted_at, "%Y-%m") as date, COUNT(*) as total')
->withTrashed() // 包含软删除的用户
->whereNotNull('deleted_at') // 只统计已删除的用户
->groupByRaw('DATE_FORMAT(deleted_at, "%Y-%m")')
->get();上述查询会返回一个集合,其中包含 date (格式为 YYYY-MM) 和 total 字段。如果2021年2月没有用户被删除,那么 2021-02 将不会出现在 $data 集合中。
为了解决数据不连续的问题,最推荐且最易于维护的方法是在获取数据库结果后,利用 PHP 和 Carbon 库进行后处理。这种方法避免了在 SQL 层面构建复杂的日历表或使用递归 CTE,从而简化了逻辑并提高了可读性。
核心思想是:
首先,定义数据的时间范围。你可以根据业务需求设定起始日期,例如从一年以前开始,或者从数据库中最早的记录日期开始。结束日期通常是当前日期。
use Carbon\Carbon;
use Illuminate\Support\Collection; // 确保引入 Collection 类
// 获取原始数据(如上文所示)
$data = User::selectRaw('DATE_FORMAT(deleted_at, "%Y-%m") as date, COUNT(*) as total')
->withTrashed()
->whereNotNull('deleted_at')
->groupByRaw('DATE_FORMAT(deleted_at, "%Y-%m")')
->get();
// 定义时间范围
$startDate = Carbon::parse('2021-01-01'); // 示例:从2021年1月开始
// 或者:$startDate = User::orderBy('deleted_at', 'asc')->first()->deleted_at->startOfMonth(); // 从最早的删除日期开始
$endDate = Carbon::now()->endOfMonth(); // 结束日期为当前月份的月末
// 创建一个可复用的函数来填充缺失月份
function fillEmptyMonths(Collection $data, Carbon $start, Carbon $end): Collection
{
$loopDate = $start->copy()->startOfMonth(); // 从起始月份的第一天开始循环
// 遍历从起始月份到结束月份的所有月份
// 使用 diffInMonths 确保循环覆盖所有月份
for ($months = 0; $months <= $start->diffInMonths($end); $months++) {
$currentMonthFormat = $loopDate->format('Y-m');
// 检查当前月份是否已存在于数据集合中
if ($data->where('date', '=', $currentMonthFormat)->isEmpty()) {
// 如果不存在,则创建一个新的 stdClass 对象作为占位符
$row = new \stdClass();
$row->date = $currentMonthFormat;
$row->total = 0; // 设置默认值为 0
$data->push($row); // 将新创建的行添加到集合中
}
$loopDate->addMonth(); // 移动到下一个月
}
// 最后,为了确保图表数据按时间顺序排列,对集合进行排序
return $data->sortBy('date')->values();
}
// 调用函数填充数据
$filledData = fillEmptyMonths($data, $startDate, $endDate);
// $filledData 现在包含所有月份的数据,缺失月份的 total 为 0
// 例如:
// [
// { "date": "2021-01", "total": 15 },
// { "date": "2021-02", "total": 0 },
// { "date": "2021-03", "total": 22 },
// // ... 更多月份
// ]通过在 Laravel 中结合 Carbon 库对数据库查询结果进行后处理,我们可以优雅且高效地解决按时间分组数据时因数据缺失导致的序列不连续问题。这种方法不仅逻辑清晰、易于维护,而且能生成完整的、适用于各种图表展示的连续时间序列数据,极大地提升了数据分析和可视化的用户体验。
以上就是填充 Laravel Eloquent 查询中缺失的月份数据的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号