2009-12-17 1 views
6

В Mysql, я хочу, чтобы выбрать нижние 2 пунктов из каждой категорииSQL запрос, чтобы выбрать нижний 2 из каждой категории

Category Value 
1  1.3 
1  4.8 
1  3.7 
1  1.6 
2  9.5 
2  9.9 
2  9.2 
2  10.3 
3  4 
3  8 
3  16 

Давая мне:

Category Value 
1  1.3 
1  1.6 
2  9.5 
2  9.2 
3  4 
3  8 

Прежде чем я мигрировали из sqlite3 я имел чтобы сначала выбрать самый низкий из каждой категории, а затем исключить все, что с этим связано, мне пришлось снова выбрать самый низкий из каждой категории. Тогда выигрывает все, что соответствует этой новой самой низкой или меньшей категории. Это также выберет более 2 в случае галстука, что было бы раздражать ... У него также было очень много времени.

Моя конечная цель - подсчитать количество раз, когда человек находится в одном из наименьших 2 категории (есть также поле имени), и это та часть, которую я не знаю, как это сделать. Благодаря

+0

Есть также идентификатор, который уникален для каждой строки? –

+1

Поскольку вам не нравятся галстуки, как вы планируете их избегать? Любое решение, к которому прибегает любой человек, должно будет каким-то образом иметь дело со связями, поэтому вы должны попытаться конкретно изложить правила, регулирующие их. –

+2

Я пометил этот вопрос с помощью 'great-n-per-group', потому что он похож на многие другие вопросы, заданные в StackOverflow с этим тегом. Хотя я понимаю, что вы запрашиваете * наименее * значения для каждой группы, техника для ее решения одинакова. –

ответ

4

Вы могли бы попробовать это:

SELECT * FROM (
    SELECT c.*, 
     (SELECT COUNT(*) 
     FROM user_category c2 
     WHERE c2.category = c.category 
     AND c2.value < c.value) cnt 
    FROM user_category c) uc 
WHERE cnt < 2 

Это должно дать вам желаемые результаты, но проверить, если производительность нормально.

+0

Это не работает. Он возвращает 9.2 и 10.3 для категории 2. –

+0

Извините, что слышу это. Я попробовал, и это работает для меня. Не могли бы вы проверить правильность тестовых данных? Благодаря! –

+0

Да, я ввел его точно так, как он появляется выше, в том же порядке и все. Значения для категории 1 верны правильно (1.3 и 1.6), но для категории 2 это неверно, а для 2 также (возвращает 4 и 16). Кроме того, этот запрос даже не выполняется, пока вы не дадите псевдоним первому подвыборке. –

1

Объединение должно работать. Я не уверен в производительности по сравнению с решением Питера.

SELECT smallest.category, MIN(smallest.value) 
    FROM categories smallest 
GROUP BY smallest.category 
UNION 
SELECT second_smallest.category, MIN(second_smallest.value) 
    FROM categories second_smallest 
    WHERE second_smallest.value > (SELECT MIN(smallest.value) FROM categories smallest WHERE second.category = second_smallest.category) 
GROUP BY second_smallest.category 
+0

В окошке where, где находится суб-выбор, есть опечатка, должна быть «WHERE smallest.category = second_smallest.category». –

+1

Кроме того, это не даст правильных результатов, если есть связь для наименьшего значения в данной категории. –

+0

Чтобы избавиться от связей, просто добавьте DISTINCT? –

8
SELECT c1.category, c1.value 
FROM catvals c1 
LEFT OUTER JOIN catvals c2 
    ON (c1.category = c2.category AND c1.value > c2.value) 
GROUP BY c1.category, c1.value 
HAVING COUNT(*) < 2; 

Испытано на MySQL 5.1.41 с тестовыми данными. Выход:

+----------+-------+ 
| category | value | 
+----------+-------+ 
|  1 | 1.30 | 
|  1 | 1.60 | 
|  2 | 9.20 | 
|  2 | 9.50 | 
|  3 | 4.00 | 
|  3 | 8.00 | 
+----------+-------+ 

(. Лишние десятичные места, потому что я объявил value колонку как NUMERIC(9,2))

Как и другие решения, это производит более 2 строк в каждой категории, если есть связи. Есть способы создать условие соединения, чтобы разрешить это, но нам нужно будет использовать первичный ключ или уникальный ключ в вашей таблице, и нам также нужно будет знать, как вас предполагают, что связи будут разрешены.

+0

это здорово! именно то, что я искал! благодаря! –

1

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

/* creating temporary variables */ 
mysql> set @cnt = 0; 
mysql> set @trk = 0; 

/* query */ 
mysql> select Category, Value 
     from (select *, 
       @cnt:=if(@trk = Category, @cnt+1, 0) cnt, 
       @trk:=Category 
       from user_categories 
       order by Category, Value) c1 
     where c1.cnt < 2; 

Вот результат.

+----------+-------+ 
| Category | Value | 
+----------+-------+ 
|  1 | 1.3 | 
|  1 | 1.6 | 
|  2 | 9.2 | 
|  2 | 9.5 | 
|  3 |  4 | 
|  3 |  8 | 
+----------+-------+ 

Это протестирован на MySQL 5.0.88 Обратите внимание, что начальное значение переменной @trk должно быть не наименьшее значение поля категории.

1

Вот решение, которое правильно обрабатывает дубликаты. Название таблицы является «ZZZ» и столбцы INT и плывут

select 
    smallest.category category, min(smallest.value) value 
from 
    zzz smallest 
group by smallest.category 

union 

select 
    second_smallest.category category, min(second_smallest.value) value 
from 
    zzz second_smallest 
where 
    concat(second_smallest.category,'x',second_smallest.value) 
    not in (-- recreate the results from the first half of the union 
     select concat(c.category,'x',min(c.value)) 
     from zzz c 
     group by c.category 
    ) 
group by second_smallest.category 

order by category 

Предостережения:

  • Если имеется только одно значение для данной категории, то только то, что одна запись возвращается.
  • Если для каждой строки был уникальный идентификатор записи, вам не понадобятся все конкаты для имитации уникального ключа.

Ваш пробег может варьироваться,

--Mark