postgres: запустить group by с условиями, специфичными для каждого условия группы? - PullRequest
0 голосов
/ 06 сентября 2018

Я использую Postgres 9.6.У меня есть таблица пользователей:

 id                            | integer                     |           | not null | nextval('user_id_seq'::regclass)
 name                          | character varying           |           | not null | 
 t_registered       | timestamp without time zone |           | not null |  

И у меня есть таблица action:

 actioned_by      | integer                     |           |          | 
 category         | character varying           |           | not null | 
 t_actioned       | timestamp without time zone |           | not null |     

Я хочу, чтобы каждый пользователь мог комментировать целочисленное поле num_days, которое указываетза сколько различных 24-часовых периодов у них есть действия в течение 30 дней после регистрации.

Я знаю, как получить общее количество отдельных календарных дней, в которые каждый пользователь был активен:

 SELECT d.actioned_by, COUNT(*) AS cnt FROM 
  (SELECT date_trunc('day', t_actioned) AS day, actioned_by 
   FROM history 
   GROUP BY day, actioned_by) d 
 GROUP BY actioned_by
 ORDER BY cnt DESC;

Но я застрял на том, как получить это в 30-дневном временном окне, специфичном для каждого пользователя.

Я подозреваю, что это продвинутый SQL!

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

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Вы можете использовать COUNT с FILTER выражением:

SELECT 
    u.id,
    u."name",
    COUNT(*) FILTER (WHERE h.t_actioned BETWEEN u.t_registered AND u.t_registered + INTERVAL'30 days')
FROM 
    history h
    JOIN "user" u ON u.id = h.actioned_by
GROUP BY
    u.id,
    u."name"

Подробнее здесь .

0 голосов
/ 06 сентября 2018
SELECT actioned_by, COUNT(*)
FROM (
        SELECT actioned_by, date_trunc('day', t_actioned)
        FROM history
        JOIN user ON id = actioned_by
        WHERE t_actioned BETWEEN t_registered AND t_registered + interval '30 days'
        GROUP BY actioned_by, date_trunc('day', t_actioned)
) sub
GROUP BY actioned_by

Итак, во-первых, вы сказали, что хотите получить количество различных дней, поэтому вы должны группировать по date_trunc, иначе вы будете считать несколько событий в один и тот же день (если они существуют).

Что делает этот запрос, присоединитесь к user, чтобы получить зарегистрированную временную метку для каждого пользователя, убедитесь, что t_actioned находится в пределах 30 дней с момента регистрации, затем сгруппируйте по пользователю и уникальной дате, и, наконец, посчитайте по каждому пользователю.

Если вы хотите включить пользователей без каких-либо действий, вы можете слегка изменить их: выберите из таблицы пользователей и оставьте историю соединений:

SELECT id, COUNT(*)
FROM (
        SELECT id, date_trunc('day', t_actioned)
        FROM user
        LEFT JOIN history
            ON id = actioned_by
            AND t_actioned BETWEEN t_registered AND t_registered + interval '30 days'
        GROUP BY id, date_trunc('day', t_actioned)
) sub
GROUP BY id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...