Обращение к группе строк

#mysql #sorting #join #sql-update #window-functions

Вопрос:

Допустим, у меня есть следующая таблица, рейтинги,

 |film_id|user_id|rank|
|-------|-------|----|
|1      |1      |2   |
|2      |1      |3   |
|3      |1      |1   |
|1      |2      |2   |
|2      |2      |1   |
|3      |2      |3   |
 

и я удаляю идентификатор фильма 2 .

Есть ли способ MySQL повторно выполнить просмотр оставшихся фильмов для каждого пользователя в одном запросе, например:

 |film_id|user_id|rank|
|-------|-------|----|
|1      |1      |2   |
|3      |1      |1   |
|1      |2      |1   | <- changed from 2 to 1
|3      |2      |2   | <- changed from 3 to 2
 

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

Другими словами, если после удаления строки рейтинг пользователя вырос на 1,2,4,5,6, я хочу, чтобы он был восстановлен последовательно, начиная с 1 (1,2,3,4,5).

Спасибо!

Комментарии:

1. Очевидный способ-пересчитать рейтинги на той же основе, на которой они были рассчитаны ранее. Здесь нет ничего, что указывало бы на то, как это было сделано.

2. @TangentiallyPerpendicular, извините, это не казалось особенно актуальным для вопроса, но я рад уточнить. Это не рассчитано само по себе. Он устанавливается пользователем. Логика приложения принимает список film_ids по порядку и обновляет рейтинг по мере необходимости. Это простой и недорогой способ установить порядок в случае сортировки пользователем. В случае обработки удаления я задался вопросом, возможно ли и быстрее обработать все детали в базе данных, поскольку вся необходимая информация уже находится в базе данных.

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

4. @TheImpaler, Может быть, я просто недооцениваю это, я не смотрю на формулу. Хотя я думаю, что так оно и есть. Я просто хочу удалить пробелы, оставшиеся после удаления строки, для каждого пользователя. Я обновил вопрос дополнительной информацией.

5. Вы хотите обновить таблицу новыми рейтингами или просто выбрать запрос? Кроме того, какова ваша версия MySQL?

Ответ №1:

Присоедините таблицу к запросу, который использует ROW_NUMBER() функцию окна для восстановления рейтинга:

 UPDATE tablename t1
INNER JOIN (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY `rank`) rn
  FROM tablename
) t2 ON t2.film_id = t1.film_id AND t2.user_id = t1.user_id
SET t1.`rank` = t2.rn
WHERE t1.`rank` <> t2.rn; -- actually this in not needed 
                          -- as MySql does not perform the update 
                          -- if the current and new values are the same
 

Смотрите демонстрацию.

Комментарии:

1. Это сработало великолепно! Спасибо. Мне нравятся подобные решения, которые позволяют обрабатывать данные там, где это наиболее эффективно, но я просто не мог этого понять. (Мне нужно провести некоторые исследования по этому OVER (PARTITION BY... пункту, потому что я этого раньше не видел.)