COUNT с GROUP BY на основе только самых последних строк - PullRequest
0 голосов
/ 03 марта 2020

У меня есть таблица с именем user_teams, которая имеет следующие столбцы:

id: primary key
user_id: FK to users table
team_id: FK to teams table
effective_date: Date

Я хочу получить запрос, который задан набор идентификаторов команд, он дает мне количество пользователей, которые в настоящее время принадлежат к этой команде. Текущая часть важна, и поэтому существует effective_date. Например, если пользователь с идентификатором 1 сначала принадлежал команде с идентификатором 1, а затем принадлежал команде с идентификатором 2, было бы две строки:

id: 1
user_id: 1
team_id: 1
effective_date: '2020-01-01'


id: 2
user_id: 1
team_id: 2
effective_date: '2020-02-01'

И этот пользователь должен быть просто посчитан для команды с идентификатором 2. У меня есть следующий запрос, который выполняет то, что я ищу, но мне не нравится, потому что подвыбор будет в основном go по всей таблице:

SELECT ut.team_id, COUNT(*) as count
  FROM (
    SELECT DISTINCT ON (ut.user_id) ut.*
    FROM user_teams ut
    ORDER BY ut.user_id, ujpjl.effective_date DESC
  ) ut
WHERE ut.team_id IN (<PASSED_TEAM_IDS>)

Я могу ' t переместить ГДЕ внутрь подвыбора, потому что, если, например, я должен использовать предыдущий пример и выполнить запрос для team_id 1:

 SELECT ut.team_id, COUNT(*) as count
      FROM (
        SELECT DISTINCT ON (ut.user_id) ut.*
        FROM user_teams ut
        WHERE ut.team_id IN (1)
        ORDER BY ut.user_id, ujpjl.effective_date DESC
      ) ut

Он будет считать пользователя, что в какой-то момент он был в команда 1, но это больше не так.

Есть ли другой способ сделать этот запрос, чтобы мой подвыбор не go прошел по всей таблице user_teams?

Ответы [ 2 ]

0 голосов
/ 03 марта 2020

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

  1. Найдите всех пользователей, которые играют или играли в запрошенных командах.
  2. Найдите этих пользователей 'последние записи.
  3. Определите команды этих записей.
  4. Сохраните только нужные команды и количество.

Запрос:

select team_id, count(*)
from
(
  select
    team_id,
    row_number() over (partition by user_id order by effective_date desc) as rn
  from user_teams
  where user_id in
  (
    select user_id
    from user_teams
    where team_id in (1,2,3)
  )
) ranked
where rn = 1 and team_id in (1,2,3)
group by team_id
order by team_id;

Индексы:

create index idx1 on user_teams (team_id, user_id);
create index idx2 on user_teams (user_id, effective_date, team_id);

В любом случае, работа таким образом имеет смысл, когда у вас есть, скажем, 10000 пользователей с историей их команды в таблице, но в команде всего пять или десять пользователей. Это означает работу с небольшим подмножеством данных таблицы. Как только соотношение станет менее экстремальным, можно просто набрать go по всей таблице, т. Е. Использовать собственный запрос. Это может быть полезно для второго индекса, поскольку он содержит все данные в соответствующем порядке (для пользователя -> самая высокая дата -> команда).

0 голосов
/ 03 марта 2020

Нет, я не думаю, что это возможно.

Как вы объяснили, вы должны учитывать и другие команды.

Я бы сказал, что ваше решение - лучшее.

...