Выберите лучшие X записей для каждой группы или по умолчанию - PullRequest
0 голосов
/ 29 октября 2018

У меня есть следующая схема:

users:

id email
1  'user.one@test.com'
2  'user.two@test.com'

video_group:

id title
1  'Group 1'
2  'Group 2'

videos:

id group_id rank title
1  1        1    'Group 1 - Video 1'
2  1        2    'Group 1 - Video 2'
3  2        1    'Group 2 - Video 1'

user_video_play_times:

video_id user_id time last_update
2        1       12   01-02-2018
1        1       120  01-01-2018

Мне нужно получить time, user_id, video_id и group_id последнего видео, воспроизведенного пользователем в определенных группах, но если для группы user_video_play_times нет записей для группы, то видео с самым низким рейтингом должно быть возвращено. Например:

user_id group_id video_id time
1       1        2        12    -- user.one + group 1
1       2        3        0     -- user one + group 2

Это мой запрос:

SELECT
   pt.user_id user_id,
   v.id       video_id,
   g.id       group_id,
   pt.time    time
FROM
   videos v
   INNER JOIN video_groups g ON g.id = v.group_id
   LEFT JOIN user_video_play_times pt ON 
      pt.video_id = v.id AND 
      pt.user_id = 1
   LEFT JOIN (
      SELECT 
         g.id AS g_id,
         MAX(pt.last_update) AS pt_last_update
      FROM
         user_video_play_times pt
         INNER JOIN videos v ON v.id = pt.video_id
         INNER JOIN video_groups g ON g.id = v.group_id
      WHERE
         pt.user_id = 1 AND
         g.id IN (1, 2)
      GROUP BY
         g.id
   ) lpt ON lpt.g_id = g.id AND lpt.pt_last_update = pt.last_update
WHERE
   g.id IN (1, 2)
GROUP BY
   g.id

Это вроде работает, но ...

  1. Добавление v.title к выбору столбца по какой-то причине портит результаты, заставляя все возвращать только видео с рангом 1. Есть идеи, почему?
  2. Может ли этот запрос быть оптимизирован, или есть другой более плавный способ достижения тех же результатов?

Любая помощь с этим очень ценится!

БД скрипка здесь

Обновление 1:

Эта проблема, кажется, возникает, только когда столбец os типа text.

1 Ответ

0 голосов
/ 29 октября 2018

Поскольку ваша db <> скрипка предназначена для MariaDB версии 10.3; Я предполагаю, что у вас есть Функции окна .

Мы можем использовать функцию Row_number() для раздела group_id, чтобы получить значения номеров строк в соответствии с определенными правилами. Видео с последним значением last_update будет иметь номер строки 1 и так далее. Если видео не воспроизводится, то у номера с наименьшим значением ранга будет номер строки = 1.

Мы можем использовать этот набор результатов в качестве производной таблицы и рассматривать только те строки, где номер строки = 1.

SELECT 
  dt.user_id, 
  dt.group_id, 
  dt.video_id, 
  dt.video_title, 
  dt.time 
FROM 
(
  SELECT
     pt.user_id AS user_id,
     g.id       AS group_id,
     v.id       AS video_id,
     v.title    AS video_title,  
     pt.time    AS time,  
     ROW_NUMBER() OVER(PARTITION BY v.group_id 
                       ORDER BY pt.last_update DESC, 
                                v.`rank` ASC) AS row_num 
  FROM videos AS v
  INNER JOIN video_groups AS g 
    ON g.id = v.group_id AND 
       g.id IN (1,2) 
  LEFT JOIN user_video_play_times AS pt 
    ON pt.video_id = v.id AND 
       pt.user_id = 1 
) AS dt 
WHERE dt.row_num = 1

Посмотреть на скрипку БД

Результат:

| user_id | group_id | video_id | video_title       | time |
| ------- | -------- | -------- | ----------------- | ---- |
| 1       | 1        | 2        | Group 1 - Video 2 | 12   |
|         | 2        | 3        | Group 2 - Video 1 |      |

PS: Обратите внимание , что Rank является зарезервированным ключевым словом , и вам действительно следует избегать его использования в качестве имени столбца / таблицы.

...