MySql是每组中第二小的元素

MySql是每组中第二小的元素,mysql,group-by,aggregate,greatest-n-per-group,Mysql,Group By,Aggregate,Greatest N Per Group,我有一张类似于以下内容的表格: 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-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个最大/最小元素的聚合函数,所以我不能使用“groupby”。更具体地说,如果我有一个神奇的MIN()聚合,它接受第二个参数“offset”,我会写:

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

有什么建议吗?

我建议你用你的n值来控制你的回报大小。 例如,假设您想要第三个最低值。。。 实际上,您所追求的是底部3个值中的最大值

因此,它将是前1名(前n名由col ASC排序)

编辑:正如@Chad Birch在评论中指出的,如果您不能在子查询中使用LIMIT,这种方法可能会有问题

编辑2: 下面是一个有趣的解决方法,使用
JOIN
s和
LIMIT

一个黑客是使用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            |
+------------+----------------------------------+
子串_索引(edate,,,,2)向前移动2个元素(第n个元素用n替换2)

我们在上面的输出上运行另一个子字符串_索引,以使用子字符串_索引(子字符串_索引(edate,,,,,,,,,,,,-1)仅获取第二个元素(中间结果的最后一个元素)

如果要计算的值太多,则可能会耗尽组\u concat\u max\u len值(默认值为1024,但可以设置得更高)

更新:上面给出的SQL将给出第n个元素,即使tht组的元素少于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;  

绝对必须在单个查询中完成?这尤其困难,因为MySQL不支持子查询中的
LIMIT
子句。最简单的方法可能是选择所有内容,并计算出您真正想要的数据库之外的记录。@chadbirch。如果我没有选择的话——我会按照你的建议去做,但是我觉得这个需求非常简单,非常有用,我可以用一个MySql查询来完成它。我可能错了,硬汉:-)标签上写着“每个组最大n个”。一些答案有一个处理MySQL中缺少的特性的通用方法,使用巧妙的技巧;生成完整组集的组应可根据选择。祝你好运找到神奇的密码。@Chad-想把你的评论作为答案吗?在阅读了一些文章后,我认为你是对的。MySQL与MSSQL的
TOP
是一个
LIMIT
子句,但它在子查询中不支持这一点,因此如果需要在一个查询中完成,那么这不是一个选项。
+------------+------------------------------------------------------+
| mdate      | substring_index(substring_index(edate,',',2),',',-1) |
+------------+------------------------------------------------------+
| 2010-01-01 | 2010-03-02                                           |
| 2010-02-01 | 2010-04-02                                           |
+------------+------------------------------------------------------+
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;