повышение производительности запроса - PullRequest
1 голос
/ 20 февраля 2020

Я создаю приложение на форуме, в котором пользователи могут публиковать сообщения. Эти сообщения могут видеть другие.

Структура таблицы (упрощенно):

// table: users
user_id | username | gender
---------------------------
1       | john     | m
2       | jane     | f
...

// table: posts
post_id | user_id | title
-------------------------
1       | 1       | Hello
...

// table: views
view_id | post_id | user_id | timestamp
---------------------------------------
1       | 1       | 2       | 2020-01-01 12:00:00
...

Теперь я хочу создать запрос, чтобы получить статистику по этому сообщению. Я хочу получить уникальные просмотры (каждый раз, когда пользователь просматривает сообщение, оно регистрируется, но я хочу подсчитать всех пользователей только один раз) и хочу получить соотношение мужчина / женщина.

SELECT
    title,
    (SELECT COUNT(DISTINCT user_id) FROM views WHERE post_id = 1) AS unique_views,
    (SELECT COUNT(user_id) FROM users WHERE gender = 'm' AND user_id IN (SELECT user_id FROM views WHERE post_id = 1) AS male_views,
    (SELECT COUNT(user_id) FROM users WHERE gender = 'f' AND user_id IN (SELECT user_id FROM views WHERE post_id = 1) AS female_views
FROM
    posts
WHERE
    post_id = 1

Запрос работает, но это запрос с 5 подзапросами. У меня пока нет большого количества данных для тестирования, но я боюсь, что производительность снизится, когда у меня будет, скажем, + 1 млн пользователей, + 1 млн постов и + 10 млн просмотров.

Другой подход состоит в том, чтобы полностью разделить запрос на несколько запросов: один для общего количества уникальных представлений, для гендерных представлений (с отличным), но тогда все равно будет всего 6 запросов.

Я использую postgresql и у меня есть индекс для users.user_id, users.gender, posts.post_id, views.view_id, views.post_id.

Вопрос: существует ли другой способ (например, с помощью JOIN) для выполнения этого запроса, и будет ли он иметь более высокую производительность при увеличении объема базы данных?

1 Ответ

2 голосов
/ 20 февраля 2020

Вы можете присоединиться и выполнить условное агрегирование вместо вложенных подзапросов:

select 
    p.title,
    count(distinct u.user_id) unique_views,
    count(u.user_id) filter(where u.gender = 'm') male_views,
    count(u.user_id) filter(where u.gender = 'f') female_views
from views v
inner join users u on u.user_id = v.user_id
inner join posts p on p.post_id = v.post_id
where p.post_id = 1
group by p.post_id, p.title
...