jey_val_star
@jey_val_star
Программист

Что лучше: больше полей или больше Join?

Есть таблица посты (posts).
На сайте, у каждого поста, выводятся кнопки соц сетей (поделиться в VK, telegram и тд)

Задача - отлавливать клики по кнопкам соц сетей и сохранять в бд для дальнейшей статистики (в каком посте какую соц сеть сколько раз кликнули)

Таблица posts
id | name | title ... и прочие

Таблица socials (пока так)
id | post_id | social_name | date

выводить нужно
id поста | Название поста | vk | tg | ok | tw
(в колонках vk tg ok tw выводится кол-во кликов)

по текущей структуре таблицы socials, для каждой колонки кол-ва кликов (vk tg ok tw) нужен отдельный join

Итак вопрос:
(интересует именно производительность - дабы с возрастанием кол-ва таких кликов и кол-ва постов не отвалился запрос)
Как лучше - куча join-ов для формирования таблицы вывода или изменить таблицу socials добавив колонки vk | tg | ok | tw (и отмечать 0/1 был клик или нет) и один join с подсчетом кол-ва нужной колонки (прим.: sum(vk))

текущий запрос
SELECT 
    posts.id, 
    posts.name, 
    count(s_vk.id) as count_vk, 
    count(s_tg.id) as count_tg,
    count(s_ok.id) as count_ok,
    count(s_tw.id) as count_tw,
FROM posts
   LEFT JOIN socials as s_vk on s_vk.post_id = posts.id and s_vk.social_name = 'vk'
   LEFT JOIN socials as s_tg on s_tg.post_id = posts.id and s_tg.social_name = 'tg'
   LEFT JOIN socials as s_ok on s_ok.post_id = posts.id and s_ok.social_name = 'ok'
   LEFT JOIN socials as s_tw on s_tw.post_id = posts.id and s_tw.social_name = 'tw'
GROUP BY posts.id


Сейчас постов примерно 1000
  • Вопрос задан
  • 463 просмотра
Решения вопроса 1
@alexalexes
Куча count - возможно, но не join-ов.
SELECT 
    posts.id, 
    posts.name, 
    count(case when s_vk.social_name = 'vk' then 1 end) as count_vk, 
    count(case when s_vk.social_name = 'tg' then 1 end) as count_tg,
    count(case when s_vk.social_name = 'ok' then 1 end) as count_ok,
    count(case when s_vk.social_name = 'tw' then 1 end) as count_tw
FROM posts
   LEFT JOIN socials as s_vk on s_vk.post_id = posts.id 
GROUP BY posts.id, posts.name

В вашем случае для ускорения не подходит ни то, ни другое.
Нужно вести отдельную таблицу в качестве кеша с аналогичными полями:
posts_id,
count_vk,
count_tg,
count_ok,
count_tw
При возникновении события клика на соц. сеть - добавлять запись в socials, а также триггером плюсовать значение по полю кеш-таблицы (и предварительно создавать запись в этой таблице по post_id, если не было ранее событий).
Для необходимости сброса кеша нужно сделать хранимую процедуру для его перегенерации на основе данного запроса.
PS:
Еще бы разбить таблицу socials - на справочник соц. сетей:
id - идент. соц сети.
full_name - полное название соц. сети,
abbrev - аббревиатура, например, ОК, ВК и т.д.
tag_name - тех. название, например, ok, vk и т.д.
... - другие параметры соц. сети

И таблицу для фиксации кликов:
soc_click_events
id - идент. события,
post_id - идент. поста,
social_id - идент. соц. сети,
event_date - дата и время клика,
... - другие параметры клика

PPS:
В итоге, для фиксации событий клика и поддержания структуры базы в нормальной форме вы используйте три таблицы - posts, socials и soc_click_events.
Для решения статистических задач вы делаете отдельные кеш-таблицы и обслуживаете их либо триггерами, либо хранимыми функциями и процедурами, получая статистические данные из первичной структуры.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
vabka
@vabka
Токсичный шарпист
Больше джоинов - нужно больше индексов и запрос дольше и сложнее будет выполняться из-за необходимости джоинить и обращаться к разным областям диска.

Больше колонок - больше места занимает отдельная запись, больше места занимает вся база, нужно больше индексов, если нужно по этим колонкам фильтроваться. Запрос дольше выполняется из-за необходимости больше данных с диска читать.

+ Учитывай, какой вариант тебе будет поддерживать легче, если нужны будут какие-то изменения.

Нужно смотреть на конкретное железо и статистику, чтобы понять, какой вариант будет эффективнее.

Конкретно в этом случае мне больше нравится вариант, где будет по колонке на каждую соцсеть - так СУБД сможет в один проход и без каких-либо индексов всё посчитать.

А ещё в вашем кейсе можно посмотреть в сторону БД временных рядов.
А ещё можно вообще не фиксировать каждый отдельный клик, а хранить уже в агрегированном виде количество кликов на каждом посте (может быть как дополнение к предыдущим вариантам, чтобы ускорить запросы)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы