2011-01-26 44 views
10

在第二小的元件I具有類似於下面的表:MySQL的每個組

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 

在表中,每個日期可以具有多個「期滿」的值。我需要一個查詢返回每個日期中的第n個最小到期日。例如,對於n = 2,我希望:

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

我的問題是,據我所知,沒有聚合函數返回的第n個最大/最小的元素,所以我不能用「GROUP BY 」。更具體地說,如果我有一個神奇的MIN()聚合接受第二個參數'offset',我會寫:

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

有什麼建議嗎?

+0

絕對必須在單個查詢內完成?這特別困難,因爲MySQL不支持子查詢中的LIMIT子句。只需選擇所有內容並計算出您實際需要的數據庫之外的記錄,最終最簡單。 – 2011-01-26 20:21:47

+0

@Chad Birch。如果我沒有選擇 - 我會按照你的建議去做,但是我覺得這個要求很簡單,而且足夠有用,可以用一個MySql查詢來完成。我可能是錯的,艱難:-) – bavaza 2011-01-27 06:54:49

回答

9

一個黑客是使用GROUP_CONCAT。按日期分組並按照升序排列到期日期,並使用substring_index函數獲取第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) 

在這裏的例子中的子查詢給出以下輸出:

+------------+----------------------------------+ 
| 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)變爲2個元件向前(對於第n個元素替代2由n)的。

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

我們運行上述輸出的另一SUBSTRING_INDEX使用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           | 
+------------+------------------------------------------------------+ 

如果有太多的值Concat的你可能會用完group_concat_max_len值(默認值1024的,但可以設置更高)。

更新:上面給出的SQL將給出第n個元素,即使該組中有更少的n個元素。爲了避免sql可以修改爲:

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

我建議你拿你的n值,並用它來控制你的退貨尺寸。 例如,假設你想在第三最低值... 你實際上是後從底部的3個值

,因此最大值這將是TOP 1 FROM(TOP N ORDER BY山坳ASC)

編輯:如@Chad Birch評論所述,如果您無法在子查詢中使用LIMIT,則此方法可能存在問題。

EDIT2: 下面是使用JOIN一個有趣的解決方法與LIMIT http://lists.mysql.com/mysql/211239