如何在 laravel 视图中提高多计数查询的性能
How to improve performance of multiple count queries in a laravel view
我正在开发一款允许用户向其联系人发送消息的营销应用程序。发送消息时,会创建一个新的 "processed_message" 数据库条目。有一个列表视图显示所有活动以及每个活动发送、阻止和失败的消息数。我的问题是,在有超过 50 个包含大量消息的活动之后,此列表视图加载时间太长。
目前每个活动都有 3 个计算属性(messages_sent、messages_failed 和 messages_blocked),它们都在活动模型的 "appends" 数组中。每个属性查询给定活动的给定类型 processed_messages 的计数。
namespace App;
class Campaign
{
protected $appends = [
'messages_sent',
'messages_blocked',
'messages_failed'
];
/**
* @relationship
*/
public function processed_messages()
{
return $this->hasMany(ProcessedMessage::class);
}
public function getMessagesSentAttribute()
{
return $this->processed_messages()->where('status', 'sent')->count();
}
public function getMessagesFailedAttribute()
{
return $this->processed_messages()->where('status', 'failed')->count();
}
public function getMessagesBlockedAttribute()
{
return $this->processed_messages()->where('status', 'blocked')->count();
}
}
我还尝试一次查询所有消息或分块查询以减少查询次数,但一次获取所有 processed_messages 用于竞选会溢出内存,分块方法也太过分了慢,因为它必须使用偏移量。我考虑过使用 processed_messages 预先加载广告系列,但这显然也会占用太多内存。
namespace App\Http\Controllers;
class CampaignController extends Controller
{
public function index()
{
$start = now();
$campaigns = Campaign::where('user_id', Auth::user()->id)->orderBy('updated_at', 'desc')->get();
$ids = $campaigns->map(function($camp) {
return $camp->id;
});
$statistics = ProcessedMessage::whereIn('campaign_id', $ids)->select(['campaign_id', 'status'])->get();
foreach($statistics->groupBy('campaign_id') as $group) {
foreach($group->groupBy('status') as $messages) {
$status = $messages->first()->status;
$attr = "messages_$status";
$campaign = $campaigns->firstWhere('id', $messages->first()->campaign_id);
$campaign->getStatistics()->$attr = $status;
}
}
return view('campaign.index', [
'campaigns' => $campaigns
]);
}
}
我的主要目标是大大减少当前页面加载时间(当有大量活动时,这可能需要 30 秒到 5 分钟)。
您可以使用 withCount
方法在不加载关系的情况下计算所有对象。
参考:
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
在你的控制器中你可以这样做:
$count = Campaign::withCount(['processed_messages' => function ($query) {
$query->where('content', 'sent');
}])->get();
您也可以在同一关系中进行多次计数:
$campaigns = Campaign::withCount([
'processed_messages',
'processed_messages as sent_message_count' => function ($query) {
$query->where('content', 'sent');
}],
'processed_messages as failed_message_count' => function ($query) {
$query->where('status', 'failed');
}],
'processed_messages as blocked_message_count' => function ($query) {
$query->where('status', 'blocked');
}])->get();
您可以通过以下方式访问计数:
echo $campaigns[0]->sent_message_count
我正在开发一款允许用户向其联系人发送消息的营销应用程序。发送消息时,会创建一个新的 "processed_message" 数据库条目。有一个列表视图显示所有活动以及每个活动发送、阻止和失败的消息数。我的问题是,在有超过 50 个包含大量消息的活动之后,此列表视图加载时间太长。
目前每个活动都有 3 个计算属性(messages_sent、messages_failed 和 messages_blocked),它们都在活动模型的 "appends" 数组中。每个属性查询给定活动的给定类型 processed_messages 的计数。
namespace App;
class Campaign
{
protected $appends = [
'messages_sent',
'messages_blocked',
'messages_failed'
];
/**
* @relationship
*/
public function processed_messages()
{
return $this->hasMany(ProcessedMessage::class);
}
public function getMessagesSentAttribute()
{
return $this->processed_messages()->where('status', 'sent')->count();
}
public function getMessagesFailedAttribute()
{
return $this->processed_messages()->where('status', 'failed')->count();
}
public function getMessagesBlockedAttribute()
{
return $this->processed_messages()->where('status', 'blocked')->count();
}
}
我还尝试一次查询所有消息或分块查询以减少查询次数,但一次获取所有 processed_messages 用于竞选会溢出内存,分块方法也太过分了慢,因为它必须使用偏移量。我考虑过使用 processed_messages 预先加载广告系列,但这显然也会占用太多内存。
namespace App\Http\Controllers;
class CampaignController extends Controller
{
public function index()
{
$start = now();
$campaigns = Campaign::where('user_id', Auth::user()->id)->orderBy('updated_at', 'desc')->get();
$ids = $campaigns->map(function($camp) {
return $camp->id;
});
$statistics = ProcessedMessage::whereIn('campaign_id', $ids)->select(['campaign_id', 'status'])->get();
foreach($statistics->groupBy('campaign_id') as $group) {
foreach($group->groupBy('status') as $messages) {
$status = $messages->first()->status;
$attr = "messages_$status";
$campaign = $campaigns->firstWhere('id', $messages->first()->campaign_id);
$campaign->getStatistics()->$attr = $status;
}
}
return view('campaign.index', [
'campaigns' => $campaigns
]);
}
}
我的主要目标是大大减少当前页面加载时间(当有大量活动时,这可能需要 30 秒到 5 分钟)。
您可以使用 withCount
方法在不加载关系的情况下计算所有对象。
参考:
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
在你的控制器中你可以这样做:
$count = Campaign::withCount(['processed_messages' => function ($query) {
$query->where('content', 'sent');
}])->get();
您也可以在同一关系中进行多次计数:
$campaigns = Campaign::withCount([
'processed_messages',
'processed_messages as sent_message_count' => function ($query) {
$query->where('content', 'sent');
}],
'processed_messages as failed_message_count' => function ($query) {
$query->where('status', 'failed');
}],
'processed_messages as blocked_message_count' => function ($query) {
$query->where('status', 'blocked');
}])->get();
您可以通过以下方式访问计数:
echo $campaigns[0]->sent_message_count