2011-01-26 36 views
10

Tôi có một bảng tương tự như sau:MySql Yếu tố thứ hai-nhỏ nhất trong mỗi nhóm

date | expiry 
-------------------------  
2010-01-01 | 2010-02-01 
2010-01-01 | 2010-03-02 
2010-01-01 | 2010-04-04 
2010-02-01 | 2010-03-01 
2010-02-01 | 2010-04-02 

Trong bảng, mỗi ngày có thể có nhiều giá trị 'hết thời'. Tôi cần một truy vấn trả về thời hạn hết hạn thứ n trong mỗi ngày. Ví dụ, với n = 2, tôi sẽ mong đợi:

 date | expiry 
-------------------------  
2010-01-01 | 2010-03-02 
2010-02-01 | 2010-04-02 

rắc rối của tôi là AFAIK, không có chức năng tổng hợp mà trả về n'th phần tử lớn nhất/nhỏ nhất, vì vậy tôi không thể sử dụng 'GROUP BY '. Cụ thể hơn, nếu tôi đã có một MIN() tổng hợp huyền diệu mà chấp nhận một tham số thứ hai 'bù đắp', tôi sẽ viết:

SELECT MIN(expiry, 1) FROM table WHERE date IN ('2010-01-01', '2010-02-01') GROUP BY date 

Bất kỳ lời đề nghị?

+0

Tuyệt đối phải được thực hiện trong một truy vấn? Nó đặc biệt khó khăn vì MySQL không hỗ trợ mệnh đề 'LIMIT' bên trong các truy vấn phụ. Nó có thể kết thúc được đơn giản nhất để chỉ cần chọn tất cả mọi thứ và làm việc ra bản ghi mà bạn thực sự muốn bên ngoài cơ sở dữ liệu. –

+0

@Chad Birch. Nếu tôi không có sự lựa chọn - tôi sẽ làm như bạn gợi ý, nhưng tôi cảm thấy yêu cầu đơn giản và hữu ích đủ để tôi có thể làm điều đó với một truy vấn MySql đơn lẻ. Tôi có thể sai, khó khăn :-) – bavaza

+0

Được gắn thẻ với 'lớn nhất-n-mỗi nhóm'.Một số câu trả lời có một cách chung để xử lý tính năng thiếu này trong MySQL bằng cách sử dụng các thủ thuật thông minh; những người tạo ra một nhóm toàn bộ nên được lựa chọn chống lại. Chúc may mắn tìm mã ma thuật. –

Trả lời

9

Một Hack là sử dụng group_concat. Nhóm theo ngày và concat ngày hết hạn theo thứ tự tăng dần và sử dụng hàm substring_index để tìm nạp giá trị thứ n.

mysql> select * from expiry; 
+------------+------------+ 
| date  | expiry  | 
+------------+------------+ 
| 2010-01-01 | 2010-02-01 | 
| 2010-01-01 | 2010-03-02 | 
| 2010-01-01 | 2010-04-04 | 
| 2010-02-01 | 2010-03-01 | 
| 2010-02-01 | 2010-04-02 | 
+------------+------------+ 
5 rows in set (0.00 sec) 

mysql> SELECT mdate, 
     Substring_index(Substring_index(edate, ',', 2), ',', -1) AS exp_date 
FROM (SELECT `date`    AS mdate, 
       GROUP_CONCAT(expiry order by expiry asc separator ",") AS edate 
     FROM expiry 
     GROUP BY mdate) e1; 
+------------+------------+ 
| mdate  | exp_date | 
+------------+------------+ 
| 2010-01-01 | 2010-03-02 | 
| 2010-02-01 | 2010-04-02 | 
+------------+------------+ 
2 rows in set (0.00 sec) 

Trong ví dụ ở đây là phụ truy vấn cho kết quả như sau:

+------------+----------------------------------+ 
| mdate  | edate       | 
+------------+----------------------------------+ 
| 2010-01-01 | 2010-02-01,2010-03-02,2010-04-04 | 
| 2010-02-01 | 2010-03-01,2010-04-02   | 
+------------+----------------------------------+ 

substring_index (EDATE, '', 2) đi 2 yếu tố về phía trước (đối với thứ n yếu tố thay thế 2 bằng n) .

+------------+------------------------------+ 
| mdate  | substring_index(edate,',',2) | 
+------------+------------------------------+ 
| 2010-01-01 | 2010-02-01,2010-03-02  | 
| 2010-02-01 | 2010-03-01,2010-04-02  | 
+------------+------------------------------+ 

chúng tôi chạy một substring_index trên đầu ra ở trên để nhận được chỉ là yếu tố thứ 2 (yếu tố cuối cùng của kết quả trung gian) sử dụng substring_index (substring_index (EDATE, '', 2), '', - 1)

+------------+------------------------------------------------------+ 
| mdate  | substring_index(substring_index(edate,',',2),',',-1) | 
+------------+------------------------------------------------------+ 
| 2010-01-01 | 2010-03-02           | 
| 2010-02-01 | 2010-04-02           | 
+------------+------------------------------------------------------+ 

Nếu có quá nhiều giá trị để concat bạn có thể hết giá trị group_concat_max_len (mặc định 1024, nhưng có thể đặt cao hơn).

CẬP NHẬT: SQL được cung cấp ở trên sẽ cung cấp phần tử thứ n ngay cả khi có ít phần tử n hơn cho nhóm tht. Để tránh điều đó, bạn có thể sửa đổi sql là:

SELECT mdate, 
     IF(cnt >= 2,Substring_index(Substring_index(edate, ',', 2), ',', -1),NULL) AS exp_date 
FROM (SELECT `date`    AS mdate, 
       count(expiry) as cnt, 
       GROUP_CONCAT(expiry order by expiry asc separator ",") AS edate 
     FROM expiry 
     GROUP BY mdate) e1; 
0

Tôi đề nghị bạn lấy giá trị n và sử dụng nó để kiểm soát kích thước trả lại của bạn. Ví dụ, nói rằng bạn muốn giá trị thứ ba thấp nhất ... Những gì bạn đang thực sự sau là giá trị lớn nhất từ ​​3 giá trị dưới

Vì vậy, nó sẽ là TOP 1 TỪ (TOP n ORDER BY col ASC)

EDIT: như đã nêu trong nhận xét của @Chad Birch, phương pháp này có thể có vấn đề nếu bạn không thể sử dụng LIMIT bên trong các truy vấn phụ.

EDIT2: Dưới đây là một cách thú vị sử dụng JOIN s với LIMIT http://lists.mysql.com/mysql/211239

+0

MySQL tương đương với 'TOP' của MSSQL là một mệnh đề' LIMIT', nhưng nó không hỗ trợ điều này trong các truy vấn con, vì vậy đây không phải là một tùy chọn nếu nó cần được thực hiện trong một truy vấn. –

Các vấn đề liên quan