MySQL - jumlah maksimum selama bulan yang berbeda dengan ikatan selama beberapa tahun

9

Pertanyaan ini diinspirasi oleh yang satu ini [ditutup] dan hampir identik dengan yang satu ini tetapi menggunakan RDBMS yang berbeda (PostgreSQL vs MySQL).

Misalkan saya memiliki daftar tumor (data ini disimulasikan dari data nyata):

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

Anda ingin mengetahui tumor mana yang paling umum pada bulan tertentu - sejauh ini sangat baik!

Sekarang, Anda akan melihat bahwa untuk bulan 1 tahun 2017, ada dasi - jadi tidak masuk akal untuk memilih secara acak dan memberikannya sebagai jawaban - jadi ikatan harus disertakan - ini membuat masalah jauh lebih menantang.

Jawaban yang benar adalah:

  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

Bonus lebih lanjut adalah memiliki nama bulan muncul sebagai teks daripada bilangan bulat.

Saya punya solusi tetapi cukup rumit - Saya ingin tahu apakah solusi saya optimal atau tidak. Biola MySQL ada di sini !

Vérace
sumber
Saya mengerti ini adalah pertanyaan khusus SQL, tetapi ini bisa dibuat lebih sederhana dengan menggunakan database deret waktu.
Sash
2
@Sash, ini bisa dilakukan dengan lebih sederhana dengan sebagian besar DBMS SQL, termasuk versi MySQL / MariaDB yang lebih baru. MySQL 5.6 tidak mengimplementasikan banyak fungsi yang ditemukan setelah SQL92.
Lennart

Jawaban:

4

Upaya saya untuk menyelesaikan ini adalah sebagai berikut. Saya akan sangat menghargai saran tentang bagaimana permintaan ini dapat ditingkatkan:

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

Dan itu memang memberikan hasil yang benar, seperti yang bisa dilihat di biola di sini !

Vérace
sumber
Saya pikir itu tidak mungkin dilakukan dengan lebih sederhana. Salah satu alternatif yang muncul dalam pikiran adalah sub-pilih alih-alih bergabung untuk mendapatkan jumlah yang sama dengan jumlah maksimum untuk tahun dan tanggal. Mungkin, tapi tidak sesederhana itu. Pilihan lain adalah menggunakan variabel untuk meniru peringkat () lebih dari partisi dengan ...) dan berharap Anda telah menemukan pekerjaan baru pada saat permintaan harus diubah ;-)
Lennart
Semoga kita berada di MySQL 8 sebelum hal seperti itu terjadi :-). Ini akhirnya membawa MySQL ke dalam abad ke-21! Analytics, CTE, REGEXP yang tepat - terlihat bagus - meskipun Anda tidak dapat melakukan INTERSECT dan beberapa keluhan lainnya, tetapi sepertinya Oracle benar-benar telah memasukkan banyak ke dalam rilis ini.
Vérace
0

Menggunakan MySQL-8.0 dan CTE pertama-tama kita buat tmpsebagai pengelompokan jumlah agregat berdasarkan tahun / bulan / nature_of_illness, RANK()memberikan nilai yang identik dengan nilai cyang sama sehingga duplikat maks diperhitungkan:

 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
sumber