Here is my query:
$notifications = \App\Notification::query()->where('users_scope', 'customer')
->with('userSeen')
->where(function ($query) use ($user) {
$query->where('user_id_to', 20288)
->orWhere(function($q) use ($user) {
$q->Where('user_id_to', null)
->Where(function($q) use ($user) {
$q->where('expire_at', null)->where('created_at', '>=', "2020-04-03 04:18:42");
$q->orWhere('expire_at', '!=', null)->where('expire_at', '>', Carbon::now());
});
});
})
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])->orderBy('id', 'desc')->limit(20)->get();
Also here is the relation (used as with()
):
public function userSeen()
{
return $this->belongsToMany(User::class, NotificationSeen::class, 'notification_id', 'user_id');
}
Here is the scenario: Query above gets the last 20 user's notifications (latest notifications list). Recently, the size of the notifications
table is increased too much. It has over 8 million rows at the moment. And the query takes over 10 seconds to be executed.
Noted that, all needed indexes have been created as well on both tables (notifications
and userSeen
tables). Also, that relation (userSeen
) is like a pivot table that indicates the user either has seen the notification or not.
Any idea how can I rewrite that query to be more optimal?
Explanations about the logic:
20288
is hardcoded and will be$user->id
in reality.- when
user_id_to
isnull
, it means it's a bulk notification (must be visible for all users) - User can see bulk notifications if they have a bigger
created_at
value than the user'screated_at
. - Sometimes bulk notifications has an expire time (like marketing campaigns) that must be shown to the users if still not outdated.