PHP / MYSQL — подсчет того, сколько последовательных результатов предыдущих недель имеют то же значение, что и текущая неделя (музыкальные чарты)

#php #mysql #recursion #count #subquery

#php #MySQL #рекурсия #граф #подзапрос

Вопрос:

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

  • название песни
  • Дата
  • Позиция в чарте
  • количество предыдущих недель, в течение которых он находился в том же положении (дополнительные очки брауни, если ответ равен 0, означают, что он вырос или упал на графике)
ID название песни_ Дата chart_position
1 Танцующая королева 2020-01-19 1
2 Подражатель 2020-01-19 2
3 Танцующая королева 2020-01-12 1
4 Shape of You 2020-01-12 2
5 Ослепительный свет 2020-01-05 1
6 Подражатель 2020-01-05 2
7 Ослепительный свет 2019-12-29 1
8 Shape of You 2019-12-29 2
9 Ослепительный свет 2019-12-22 1
10 Подражатель 2019-12-22 2

Итак, учитывая простой выбор:

SELECT song_name, date, chart_position FROM table WHERE date = '2019-12-29' ORDER BY chart_position ASC

Мы должны получить следующий результат:

название песни_ Дата chart_position
Ослепительный свет 2019-12-29 1
Shape of You 2019-12-29 2

Однако, что нужно добавить дополнительную информацию, чтобы сделать это:

название песни_ Дата chart_position weeks_in_position движение (необязательно — то же / новое / вверх / вниз)
Ослепительный свет 2019-12-29 1 2 То же
Shape of You 2019-12-29 2 1 новое

Любая помощь очень ценится, поскольку я провел последние 6 часов, пытаясь разобраться в себе с большим количеством поисковых запросов в Интернете и не смог с этим справиться! Спасибо вам за уделенное время.

Ответ №1:

Скорее всего, некоторые оптимизации все еще можно выполнить, но следующие дадут вам весь результат, который вы запросили. Первая часть (CTE) в основном существует для вычисления того, сколько недель подряд песня находилась на позиции песни. Вторая часть служит для вычисления позиции по сравнению с предыдущей неделей путем выполнения соединения с таблицей за предыдущую неделю.

 WITH RECURSIVE cte AS (
  SELECT id, song_name, 1 as weeks_in_position, chart_position, dt
  FROM charts WHERE dt='2019-12-29' 
  UNION ALL
  SELECT charts.id, charts.song_name, cte.weeks_in_position 1, charts.chart_position, charts.dt
  FROM cte 
  INNER JOIN charts ON charts.song_name = cte.song_name
  AND charts.chart_position = cte.chart_position
  AND cte.id <> charts.id
  AND DATEDIFF(cte.dt, charts.dt) <= 7
  AND DATEDIFF(cte.dt, charts.dt) > 0
)

SELECT * FROM (
    SELECT cte.song_name, cte.dt, 
    MAX(cte.weeks_in_position) OVER(PARTITION BY song_name) weeks_in_position,
    CASE 
        WHEN charts.dt IS NULL THEN 'new'
        WHEN cte.chart_position > charts.chart_position THEN 'up'
        WHEN cte.chart_position < charts.chart_position THEN 'down'    
        ELSE 'same'
    END AS movement
    FROM cte 
    LEFT JOIN charts 
    ON cte.song_name = charts.song_name
    AND DATE_ADD(charts.dt, INTERVAL 7 DAY)=cte.dt
) AS DATA
WHERE dt='2019-12-29' 
 

Вы можете проверить результаты в этой скрипке db.

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

1. Спасибо, это отлично сработало с несколькими изменениями (для объединения дополнительных таблиц). Я действительно ценю вашу помощь @vixducis

Ответ №2:

Вы можете добавить новый столбец в свою таблицу для простого запроса, например, previous_chart_position типа integer (простое сравнение для same / new / up / down) и chart_position_updatedat datetime, чтобы вычислить разницу во времени для weeks_in_position .

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

1. Смысл реляционной базы данных в том, чтобы не иметь избыточных данных. И хотя в модели данных OP уже есть избыточные данные (было бы лучше переместить песни в отдельную таблицу), добавление дополнительной избыточности не является хорошим решением, когда вы можете получить требуемые данные с помощью запроса.

2. Спасибо за комментарий @vixducis — мои данные фактически разделены между 2 таблицами (с названием песни и информацией в одной, а данные этой диаграммы в другой связаны с идентификатором вместо имени). Однако для простоты понимания в этом посте я просто объединил информацию (и планировал сам немного изменить ответ по мере необходимости). 🙂