MySQL - максимум суммы за разные месяцы со связями за несколько лет


9

Этот вопрос был вдохновлен этот [закрыт] и практически идентичен этому одному , но с использованием различных СУБД ( в PostgreSQL vs. MySQL).

Предположим, у меня есть список опухолей (эти данные смоделированы из реальных данных):

CREATE table illness (nature_of_illness VARCHAR(25), created_at DATETIME);

INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Lung',   '2018-01-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Cervix', '2018-02-03 17:50:32');
-- 2017, with 1 Cervix and Lung each for the month of Jan - tie!
INSERT INTO illness VALUES ('Cervix', '2017-01-03 15:45:40');
INSERT INTO illness VALUES ('Lung',   '2017-01-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung',   '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Cervix', '2017-02-03 17:50:32');

Вы хотите узнать, какая именно опухоль была наиболее распространенной в течение определенного месяца - пока все хорошо!

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

Правильный ответ:

  Year    Month  Tumour count      Type
  2017        1             1    Cervix  -- note tie
  2017        1             1      Lung  --   "   "
  2017        2             3      Lung
  2018        1             5    Cervix
  2018        2             3      Lung

Еще одним бонусом было бы, чтобы название месяца отображалось в виде текста, а не целого числа.

У меня есть решение, но оно довольно сложное - я хотел бы знать, является ли мое решение оптимальным или нет. MySQL скрипка здесь !


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

2
@Sash, это можно сделать намного проще с большинством СУБД SQL, включая более новые версии MySQL / MariaDB. MySQL 5.6 не реализует много функциональности, изобретенной после SQL92.
Леннарт

Ответы:


4

Моя попытка решить это заключается в следующем. Буду признателен за любые советы по улучшению этого запроса:

SELECT 
  t3.c_year AS "Year",
  t3.c_month AS "Month", 
  t3.il_mc AS  "Tumour count", 
  t4.ill_nat AS "Type" FROM
(
  SELECT c_year, c_month, il_mc FROM
  (
    SELECT  
    c_year, 
    c_month,
    MAX(month_count) AS il_mc
  FROM
    (
      SELECT nature_of_illness as illness,
        EXTRACT(YEAR  FROM created_at) AS c_year,
        EXTRACT(MONTH FROM created_at) AS c_month,
        COUNT(EXTRACT(MONTH FROM created_at)) AS month_count
      FROM illness
      GROUP BY illness, c_year, c_month
      ORDER BY c_year, c_month
    ) AS t1
  GROUP BY c_year, c_month
  ) AS t2
) AS t3
JOIN
(
SELECT 
  EXTRACT(YEAR FROM created_at) AS t_year, 
  EXTRACT(MONTH FROM created_at) AS t_month,  
  nature_of_illness AS ill_nat, 
  COUNT(nature_of_illness) AS ill_cnt
FROM illness
GROUP BY t_year, t_month, nature_of_illness
ORDER BY t_year, t_month, nature_of_illness
) AS t4
ON t3.c_year = t4.t_year
AND t3.c_month = t4.t_month
AND t3.il_mc = t4.ill_cnt

И это дает правильный результат, как можно увидеть здесь !


Я не думаю, что это можно сделать намного проще. Одна из альтернатив, которая приходит на ум, - это дополнительный выбор вместо объединения для получения значений, равных максимальному числу для года и даты. Возможно, но вряд ли проще. Другой вариант - использовать переменные, чтобы имитировать rank () над разделом с помощью ...) и надеяться, что к моменту изменения запроса вы найдете новую работу ;-)
Lennart

Надеюсь, мы будем на MySQL 8 до того, как что-то подобное произойдет :-). Это, наконец, приносит MySQL в 21-м веке! Аналитика, CTE, правильные REGEXP - выглядят хорошо - даже если вы не можете использовать INTERSECT и некоторые другие проблемы, но похоже, что Oracle действительно много сделала в этом выпуске.
Верас

0

Используя MySQL-8.0 и CTE, мы сначала создаем tmpв качестве совокупного счетчика группировку по годам / месяцам / nature_of_illness, RANK()присваиваем одинаковые значения cодному и тому же значению, чтобы учесть дублирующийся максимум:

 SELECT y as 'Year',mon as 'Month',c as 'Tumor Count', nature_of_illness as 'Type'
 FROM (
   WITH tmp AS ( 
    SELECT YEAR(created_at) as y, MONTH(created_at) as mon, COUNT(*) as c, nature_of_illness
    FROM illness
    GROUP BY y, mon, nature_of_illness
   )
   SELECT y, mon, c, nature_of_illness,
   RANK() OVER (PARTITION BY y, mon ORDER BY c DESC) as `rank`
   FROM tmp
 ) AS tmp2 
WHERE `rank` = 1
ORDER BY y, mon
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.