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');

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

Тепер ви помітите, що для першого місяця 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 питання, але це можна зробити набагато простіше, використовуючи базу даних часових рядів.
Sash

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 - виглядає добре, хоча ви не можете зробити INTERSECTs та кілька інших ручок, але схоже, що Oracle насправді багато вклав у цю версію.
Vérace

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.