MySql второй самый маленький элемент в каждой группе - PullRequest
10 голосов
/ 26 января 2011

У меня есть таблица, подобная следующей:

    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

Моя проблема в том, что в AFAIK нет агрегатной функции, которая возвращает n-й самый большой / самый маленький элемент, поэтому я не могу использовать 'ГРУППА ПО'.Более конкретно, если бы у меня был магический агрегат MIN (), который принимает второй параметр 'offset', я бы написал:

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

Есть предложения?

Ответы [ 2 ]

10 голосов
/ 27 января 2011

Один из способов - использовать 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 для вышеприведенного вывода, чтобы получить только 2-й элемент (последний элемент промежуточного результата), используя 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                                           |
+------------+------------------------------------------------------+

Если имеется слишком много значений для конкататации, у вас может закончиться значение group_concat_max_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;  
0 голосов
/ 26 января 2011

Я предлагаю взять значение n и использовать его для контроля размера возврата.Например, предположим, что вы хотели получить третье наименьшее значение ... На самом деле вам нужно наибольшее значение из трех нижних значений

Так что это будет TOP 1 FROM (TOP n ORDER BY col ASC)

РЕДАКТИРОВАТЬ: как отмечено в комментариях @Chad Birch, этот подход может быть проблематичным, если вы не можете использовать LIMIT внутри подзапросов.

РЕДАКТИРОВАТЬ2: Вот интересный обходной путь с использованием JOINс LIMIT http://lists.mysql.com/mysql/211239

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...