пользователей таблицы имеет около 80 000 записей
друзей таблицы имеет около 900 000 записей
Есть 104 записи с именем name = 'verena'
этот запрос (точка запрос пропал, потому что он очень упрощен) очень медленный (> 20 секунд):
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.user_id OR
users.id = friends.friend_id
)
WHERE users.firstname = 'verena';
Однако, если я удалю ИЛИ внутри JOIN, запрос будет мгновенным, поэтому либо:
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.user_id
)
WHERE users.firstname = 'verena';
возвращение результатов 1487 или
SELECT users.id FROM users
LEFT JOIN friends ON (
users.id = friends.friend_id
)
WHERE users.firstname = 'verena';
возвращение результатов 2849 выполняется мгновенно (0,001 с)
Если я удалю все остальное и go прямо для
SELECT 1 FROM friends WHERE user_id = xxx OR friend_id = xxx
или
SELECT id FROM users WHERE firstname = 'verena';
эти запросы также являются мгновенными.
Установлены индексы для friends.friend_id, friends.user_id и users.firstname.
Я не понять, почему топовый запрос медленный, в то время как если вручную разбирать его и выполнять изолированные операторы, все быстро.
Мое единственное подозрение сейчас заключается в том, что MariaDB сначала присоединяется ко ВСЕМ пользователям с друзьями, а затем фильтрует WHERE. имя = 'verena', вместо желаемого поведения: сначала отфильтруйте firstname = 'verena', а затем объедините результаты с таблицей друзей, но даже тогда я не понимаю, почему удаление OR внутри условия JOIN могло бы сделать это быстро.
Я тестировал это на 2 разных машинах, одна из которых работает MariaDB 10.3.22 с кластером Galera, а другая - с MariaDB 10.4.12 без кластера Galera
По какой технической причине такой запрос в топе огромное замедление, и как мне это исправить, не разбивая SQL на несколько операторов?
Редактировать: Вот вывод EXPLAIN для него, говорящий, что он не использует индекс для таблицы друзей и просматривает через все записи, как правильно указано в комментарии Бармара:
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
| 1 | SIMPLE | users | ref | firstname | firstname | 768 | const | 104 | Using where; Using index |
| 1 | SIMPLE | friends | ALL | user_id,friend_id | NULL | NULL | NULL | 902853 | Range checked for each record (index map: 0x6) |
+------+-------------+---------+------+-------------------+-----------+---------+-------+--------+------------------------------------------------+
Есть ли способ заставить SQL использовать оба индекса или мне просто нужно принять это ограничение и обойти его, используя, например, предложение Бармара?