Laravel 查询将分组小时的所有 X 和 Y 列相加
Posted
技术标签:
【中文标题】Laravel 查询将分组小时的所有 X 和 Y 列相加【英文标题】:Laravel query add up all of X and Y columns for grouped hour 【发布时间】:2021-05-01 16:56:40 【问题描述】:在我的 Laravel 项目中,我在检索和分组一些数据时遇到了一些麻烦。我正在尝试从相关列中提取一些数据,计算每个值并提供一个分组时间段的总数,但是为了进行分组,我需要先获取数据,而我的选择并不能很好地工作对了,我错过了什么?
查询
UptimeChecks::where('user_id', 1)
->where('monitor_id', 1)
->where('checked_at', '>=', '2021-05-01 15:19:00')
->where('checked_at', '<=', '2021-05-01 17:19:00')
->orderBy('checked_at', 'asc')
->select('event', 'checked_at')
->get();
我用上面得到的数据看起来像:
Illuminate\Database\Eloquent\Collection #3447
all: [
App\Models\UptimeChecks #3446
event: "down",
checked_at: "2021-05-01 15:37:23",
,
App\Models\UptimeChecks #3445
event: "down",
checked_at: "2021-05-01 15:38:10",
,
App\Models\UptimeChecks #3444
event: "down",
checked_at: "2021-05-01 15:44:07",
,
App\Models\UptimeChecks #3443
event: "up",
checked_at: "2021-05-01 15:45:23",
,
App\Models\UptimeChecks #3442
event: "down",
checked_at: "2021-05-01 16:05:07",
,
App\Models\UptimeChecks #3441
event: "up",
checked_at: "2021-05-01 16:07:07",
,
],
我希望获取分组小时内所有“向下”事件的计数,以及同一小时内“向上”事件的计数,因此预计会返回类似以下内容:
"2021-05-01 15:00:00":
down_events: 3,
up_events: 1
,
"2021-05-01 16:00:00":
down_events: 1,
up_events: 1
不太确定我的查询如何做到这一点?
【问题讨论】:
您可以按日期分组,但日期必须采用一种格式,例如 Y-m-d 或 Y-m-d h:00:00 【参考方案1】:您可以在查询后使用收集方法groupBy()
和map()
来实现。
UptimeChecks::...->get()
->groupBy(fn($item) => $item->checked_at->format('Y-m-d H:00:00'))
->map(fn($item) => [
'down_events' => $item->where('event', 'down')->count(),
'up_events' => $item->where('event', 'up')->count(),
]);
如果您无法使用速记闭包(php 7.4 及更高版本)
UptimeChecks::...->get()
->groupBy(function ($item)
return $item->checked_at->format('Y-m-d H:00:00');
)
->map(function ($item)
return [
'down_events' => $item->where('event', 'down')->count(),
'up_events' => $item->where('event', 'up')->count(),
];
);
如果 checked_at 不是时间戳,请将其添加到模型中的 $cast
属性中。
class UptimeChecks extends Model
protected $casts = [
'checked_at' => 'datetime',
];
或者将$item->checked_at
封装在Carbon::parse()
调用中。
use Carbon\Carbon;
...
groupBy(fn($item) => Carbon::parse($item->checked_at)->format('Y-m-d H:00:00'))
【讨论】:
给我一个错误:Call to a member function format() on string
用 Carbon::parse 包裹你的 checked_at 或者 check_at 应该在你的 Model 下的 $casts 属性中
我认为checked_at 是一个时间戳。正如@PsyLogic 所说,要么将其添加到您的 $cast 属性中,要么将它们包装在 Carbon::parse 中。我将编辑答案以添加此内容。【参考方案2】:
我认为对于大数据来说,使用 RawSQL 很容易
return UptimeChecks::
selectRaw("SUM(CASE WHEN event= 'down' THEN 1 ELSE 0 END) AS down_events, ".
"SUM(CASE WHEN event= 'up' THEN 1 ELSE 0 END) AS up_events, DATE_FORMAT('checked_at', '%Y-%m-%d %H:00:00') as checkedDate ")
->groupByRaw('checkedDate')
->get();
您可以多次使用 selectRaw 来清理您的代码
return UptimeChecks::
selectRaw("SUM(CASE WHEN event= 'down' THEN 1 ELSE 0 END) AS down_events")
->selectRaw("SUM(CASE WHEN event= 'up' THEN 1 ELSE 0 END) AS up_events")
->selectRaw("DATE_FORMAT('checked_at', '%Y-%m-%d %H:00:00') as checkedDate")
->groupByRaw('checkedDate')
->get();
【讨论】:
以上是关于Laravel 查询将分组小时的所有 X 和 Y 列相加的主要内容,如果未能解决你的问题,请参考以下文章