MySQL - ผลรวมสูงสุดในแต่ละเดือนที่แตกต่างกันและมีความสัมพันธ์กันมาหลายปี


9

คำถามนี้ได้รับแรงบันดาลใจจากคนนี้ [ปิด] และเป็นจริงเหมือนกับที่นี้หนึ่งแต่ใช้ RDBMS ที่แตกต่างกัน (เทียบกับ PostgreSQL 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 ของปี 2560 มีการผูกกัน - ดังนั้นจึงไม่มีเหตุผลใดที่จะสุ่มเลือกหนึ่งตัวและให้คำตอบ - ดังนั้นต้องรวมความสัมพันธ์ด้วย - นี่ทำให้ปัญหามีความท้าทายมากขึ้น

คำตอบที่ถูกต้องคือ:

  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 DBMS ส่วนใหญ่รวมถึง MySQL / MariaDB เวอร์ชันใหม่ MySQL 5.6 ไม่ได้ใช้ฟังก์ชั่นที่ประดิษฐ์ขึ้นมากหลังจาก SQL92
Lennart

คำตอบ:


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

และมันก็ให้ผลลัพธ์ที่ถูกต้องอย่างที่เห็นในซอนี่ !


ฉันไม่คิดว่าเป็นไปได้ที่จะทำได้ง่ายกว่านี้มาก ทางเลือกหนึ่งที่อยู่ในใจคือตัวเลือกย่อยแทนการเข้าร่วมเพื่อรับจำนวนที่เท่ากับจำนวนสูงสุดของปีและวันที่ เป็นไปได้ แต่ไม่ค่อยเรียบง่าย อีกทางเลือกหนึ่งคือการใช้ตัวแปรเพื่อเลียนแบบอันดับ () เหนือพาร์ติชันโดย ... ) และหวังว่าคุณจะได้งานใหม่ตามเวลาที่มีการเปลี่ยนแปลงคิวรี ;-)
Lennart

หวังว่าเราจะใช้ MySQL 8 ก่อนสิ่งที่เกิดขึ้น :-) มันในที่สุดก็นำ MySQL ในศตวรรษที่ 21! Analytics, CTE, REGEXP ที่เหมาะสม - ดูดี - แม้ว่าคุณจะไม่สามารถ INTERSECTs และกริพเพนอื่น ๆ ได้ แต่ดูเหมือนว่า Oracle จะใส่อะไรมากมายลงในรุ่นนี้
Vérace

0

ใช้ MySQL-8.0 และ CTEs แรกที่เราสร้าง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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.