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

您想找出在给定的月份中哪种肿瘤最常见-到目前为止一切顺利!

现在,您会注意到,在2017年的第1个月中,出现平局-因此,随机选择一个并给出答案是没有意义的 -因此必须包括平局-这使问题更具挑战性。

正确答案是:

  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

Answers:


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进入二十一世纪!分析,CTE,适当的REGEXP-看起来不错-尽管您不能做INTERSECT和其他一些技巧,但是看起来Oracle在此版本中确实投入了很多。
Vérace

0

我们首先使用MySQL-8.0和CTE创建tmp按年/月/分组的总计数分组nature_of_illnessRANK()将相同的值分配给相同的值,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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.