MySQL - maksymalna suma w różnych miesiącach z powiązaniami na wiele lat

9

Ta kwestia była inspirowana przez ten [zamknięte] i jest praktycznie identyczny do tego jednego , ale przy użyciu różnych RDBMS'S (PostgreSQL vs MySQL).

Załóżmy, że mam listę guzów (dane te są symulowane na podstawie rzeczywistych danych):

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

Chcesz dowiedzieć się, który konkretny guz był najczęstszy w danym miesiącu - jak dotąd!

Zauważysz teraz, że w 1. miesiącu 2017 r. Jest remis - więc nie ma sensu losowo wybierać jednego i dawać go jako odpowiedź - więc należy uwzględnić remisy - to sprawia, że ​​problem jest znacznie trudniejszy.

Poprawna odpowiedź to:

  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

Dodatkową korzyścią byłoby wyświetlanie nazwy miesiąca jako tekstu zamiast liczby całkowitej.

Mam rozwiązanie, ale jest dość złożone - chciałbym wiedzieć, czy moje rozwiązanie jest optymalne, czy nie. Skrzypce MySQL jest tutaj !

Vérace
źródło
Rozumiem, że jest to pytanie specyficzne dla SQL, ale można to znacznie uprościć, używając bazy danych szeregów czasowych.
Skrzydła
2
@Sash, można to zrobić znacznie prościej z większością SQL DBMS, w tym nowszymi wersjami MySQL / MariaDB. MySQL 5.6 nie implementuje wielu funkcji wymyślonych po SQL92.
Lennart,

Odpowiedzi:

4

Moja próba rozwiązania tego jest następująca. Byłbym wdzięczny za wszelkie porady dotyczące ulepszenia tego zapytania:

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

I daje poprawny wynik, jak widać tutaj na skrzypcach !

Vérace
źródło
Nie sądzę, że można to zrobić znacznie prościej. Jedną z alternatyw, która przychodzi na myśl, jest podselekcja zamiast łączenia, aby uzyskać liczby równe maksymalnej liczbie dla roku i daty. Możliwe, ale nie prościej. Inną opcją jest użycie zmiennych do naśladowania rank () w stosunku do partycji do ...) i mam nadzieję, że znalazłeś nowe zadanie do czasu zmiany zapytania ;-)
Lennart
Mam nadzieję, że będziemy na MySQL 8, zanim zdarzy się coś takiego :-). W końcu wprowadza MySQL w 21 wiek! Analityka, CTE, odpowiednie REGEXPY - wygląda dobrze - nawet jeśli nie możesz wykonać INTERSECT i kilku innych problemów, ale wygląda na to, że Oracle naprawdę włożyło wiele w to wydanie.
Vérace,
0

Korzystając z MySQL-8.0 i CTE, najpierw tworzymy tmpjako grupowanie agregatów według roku / miesiąca / nature_of_illness, RANK()przypisujemy identyczne wartości do ctej samej wartości, tak aby uwzględnić maksimum duplikatu:

 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
Danblack
źródło