Используйте join(inner join)
производительность лучше, чем whereIn
подзапрос.
В MySQL подвыборы в предложении IN повторно выполняются для каждой строки во внешнем запросе, в результате чего создается O(n^2)
.
Я думаю, что * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 101. * * * * * * * *. указанным user ($id)
, вы можете использовать этот метод напрямую:
User::where('id', $id)->first()->blockedUsers();
Подумайте сначала о применении where('verified', 1)
, поэтому вы можете использовать запрос как User::where('verified', 1)->areBlockedBy(auth()->id())
, область действия может быть такой:
public function scopeAreBlockedBy($query, $id)
{
return $query->whereHas('blockedByUsers', function($users) use($id) {
$users->where('ignore_lists.user_id', $id);
});
}
// better performance: however, when you apply another where condition, you need to specify the table name ->where('users.verified', 1)
public function scopeAreBlockedBy($query, $id)
{
return $query->join('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.blocked_user_id', '=', 'users.id')
->where('ignore_lists.user_id', $id);
})->select('users.*')->distinct();
}
Мы используем join
для второго запроса, который повысит производительность, поскольку ему не нужно использовать where exists
.
Пример для 300 000 записей в таблице пользователей:
Объясните первый запрос whereHas
, который сканирует 301119+1+1
строк и занимает 575ms
:
Объяснить второй запрос join
, который сканирует 3+1
строк и занимает 10.1ms
:
2) Включить пользователей, которые не заблокированы указанным user ($id)
, вы можете использовать whereDoesntHave
закрытие, как этот:
public function scopeNotBlockedUsers($query, $id)
{
return $query->whereDoesntHave('blockedByUsers', function($users) use ($id){
$users->where('ignore_lists.user_id', $id);
});
}
Я предпочитаю использовать whereDoesntHave
вместо leftJoin
здесь. Потому что, когда вы используете leftjoin
, как показано ниже:
User::leftjoin('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.blocked_user_id', '=', 'users.id')
->where('ignore_lists.user_id', $id);
})->whereNull('ignore_lists.id')->select('users.*')->distinct()->get();
Mysql необходимо создать временную таблицу для хранения всех записей пользователей и объединить некоторые ignore_lists
. И затем отсканируйте эти записи и найдите записи, которые без ignore_lists
. whereDosentHave
будет сканировать всех пользователей тоже. Для моего mysql сервера where not exists
немного быстрее, чем left join
. План его выполнения кажется хорошим. Производительность этих двух запросов не сильно отличается.
Для whereDoesntHave
более читабельно. Я выберу whereDoesntHave
.
3) Чтобы включить пользователей, которые заблокировали указанный user ($id)
, использовать whereHas
заблокированных пользователей, например:
public function scopeWhoBlocked($query, $id)
{
return $query->whereHas('blockedUsers', function($q) use ($id) {
$q->where('ignore_lists.blocked_user_id', $id);
});
}
// better performance: however, when you apply another where condition, you need to specify the table name ->where('users.verified', 1)
public function scopeWhoBlocked($query, $id)
{
return $query->join('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.user_id', '=', 'users.id')
->where('ignore_lists.blocked_user_id', $id);
})->select('users.*')->distinct();
}
4) Чтобы включить пользователей, которые не блокировали указанное user ($id)
, используйте whereDoesntHave
для заблокированного пользователя:
public function scopeWhoDidNotBlock($query, $id)
{
return $query->whereDoesntHave('blockedUsers', function($q) use ($id) {
$q->where('ignore_lists.blocked_user_id', $id);
});
}
PS: Не забудьте добавить индекс для foreign_key
для ignore_lists
Таблица.