Двойной JOIN из двух таблиц
От: x-code  
Дата: 20.01.24 22:47
Оценка:
В базе SQlite имеются таблицы (упрощенно): posts(to_id, from_id, text), users (id, name), groups (id, name).
Для того чтобы вывести posts в удобочитаемом виде, нужно заменить to_id и from_id на имена.
Оба идентификатора to_id и from_id могут указывать как на юзера, так и на группу, причем в posts у юзеров положительные id, а у групп отрицательные (так сделано к примеру в соцсети vk).

Провел эксперимент с таким запросом (LEFT JOIN потому что не все to_id, from_id существуют в таблицах users и groups, а записи все равно нужны):
SELECT
   CASE WHEN posts.to_id   > 0 THEN u_to.name   ELSE g_to.name   END AS to_name,
   CASE WHEN posts.from_id > 0 THEN u_from.name ELSE g_from.name END AS from_name,
   posts.to_id,
   posts.from_id,
   posts.text
FROM posts
LEFT JOIN users  AS u_to   ON posts.to_id   =  u_to.id
LEFT JOIN groups AS g_to   ON posts.to_id   = -g_to.id
LEFT JOIN users  AS u_from ON posts.from_id =  u_from.id
LEFT JOIN groups AS g_from ON posts.from_id = -g_from.id
WHERE ... ORDER BY ... LIMIT ... OFFSET ...

и оказалось, что на последних страницах пагинации все это достаточно заметно подтормаживает (хотя данных пока не много — 5000 постов, 300000 юзеров и 4000 групп).

Далее попытался выяснить что именно подтормаживает. Убрал условный JOIN, приняв для эксперимента что to_id всегда группа а from_id всегда юзер (т.е. осталось 2 джойна вместо 4). Не помогло, задержки остались. А вот если принять, что to_id и from_id всегда юзеры, и убрать группы из запроса вообще (опять же 2 джойна вместо 4), то скорость становится нормальной. Получается, что дополнительные джойны даже при небольшом размере таблицы существенно замедляет работу запроса.

Прав ли я в своих рассуждениях? Все ли я правильно делаю? Есть ли альтернативные решения этой задачи в целом?

Думаю о варианте вообще объединить юзеров и группы в одну таблицу — у них значительная часть полей общая, в 99% случаев только она и нужна, а отличающиеся поля оставить в добавочных таблицах, связанных "один к одному" с главной.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.