MySQL - tổng số tiền tối đa trong các tháng khác nhau với các mối quan hệ trong nhiều năm


9

Câu hỏi này được lấy cảm hứng từ một này [đóng] và là hầu như giống hệt nhau này một nhưng sử dụng RDBMS khác nhau của (PostgreSQL vs MySQL).

Giả sử tôi có một danh sách các khối u (dữ liệu này được mô phỏng từ dữ liệu thực):

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

Bạn muốn tìm ra khối u đặc biệt nào là phổ biến nhất trong một tháng nhất định - cho đến nay là rất tốt!

Bây giờ, bạn sẽ nhận thấy rằng trong tháng 1 năm 2017, có một chiếc cà vạt - vì vậy sẽ không có ý nghĩa gì khi chọn ngẫu nhiên một cái và đưa ra câu trả lời - vì vậy phải đưa vào các mối quan hệ - điều này khiến vấn đề trở nên khó khăn hơn nhiều.

Đáp án đúng là:

  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

Một phần thưởng nữa sẽ có tên tháng xuất hiện dưới dạng văn bản chứ không phải là số nguyên.

Tôi có một giải pháp nhưng nó khá phức tạp - Tôi muốn biết liệu giải pháp của mình có tối ưu hay không. Fiddle MySQL là ở đây !


Tôi hiểu đây là một câu hỏi cụ thể về SQL, nhưng điều này có thể được thực hiện đơn giản hơn nhiều bằng cách sử dụng cơ sở dữ liệu chuỗi thời gian.
Sash

2
@Sash, nó có thể được thực hiện đơn giản hơn nhiều với hầu hết các DBMS SQL, bao gồm cả các phiên bản mới hơn của MySQL / MariaDB. MySQL 5.6 không triển khai nhiều chức năng được phát minh sau SQL92.
Lennart

Câu trả lời:


4

Nỗ lực của tôi để giải quyết điều này là như sau. Tôi sẽ đánh giá cao bất kỳ lời khuyên nào về cách cải thiện truy vấn này:

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

Và nó cho kết quả chính xác, như có thể thấy trong câu đố ở đây !


Tôi không nghĩ rằng nó có thể làm đơn giản hơn nhiều. Một lựa chọn khác xuất hiện trong tâm trí là lựa chọn phụ thay vì tham gia để có được số lượng bằng với số lượng tối đa cho năm và ngày. Có thể, nhưng hầu như không đơn giản. Một tùy chọn khác là sử dụng các biến để bắt chước thứ hạng () trên phân vùng theo ...) và hy vọng rằng bạn đã tìm thấy một công việc mới vào thời điểm truy vấn phải được thay đổi ;-)
Lennart

Hy vọng rằng chúng tôi sẽ có mặt trên MySQL 8 trước khi mọi thứ như vậy xảy ra :-). Nó cuối cùng đã mang lại MySQL vào thế kỷ 21! Analytics, CTE, REGEXP phù hợp - có vẻ tốt - mặc dù bạn không thể thực hiện INTERSECT và một vài cách nắm bắt khác, nhưng có vẻ như Oracle đã thực sự đưa rất nhiều vào bản phát hành này.
Vérace

0

Sử dụng MySQL-8.0 và CTE, trước tiên chúng tôi tạo ra tmpkhi tổng số nhóm theo năm / tháng / nature_of_illness, RANK()gán các giá trị giống hệt nhau cho ccùng một giá trị để tối đa trùng lặp được tính:

 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.