2010-10-22 3 views
12

У меня возникла очень распространенная проблема, касающаяся «Выбор верхних N строк для каждой группы в таблице».Выбор вершин N строк для каждой группы в таблице

Рассмотрите таблицу с колонками id, name, hair_colour, score.

Я хочу набор результатов, чтобы при каждом цвете волос получить мне 3 наименования бомбардиров.

Чтобы решить эту проблему я получил именно то, что мне нужно на Rick Osborne's blogpost "sql-getting-top-n-rows-for-a-grouped-query"

Это решение не работает, как ожидалось, когда мои оценки равны.

В приведенном выше примере результат выполняется следующим образом.

id name hair score ranknum 
--------------------------------- 
12 Kit Blonde 10 1 
    9 Becca Blonde 9 2 
    8 Katie Blonde 8 3 
    3 Sarah Brunette 10 1  
    4 Deborah Brunette 9 2 - ------- - - > if 
    1 Kim Brunette 8 3 

Рассмотрите ряд 4 Deborah Brunette 9 2. Если у этого также есть тот же счет (10), что и у Сары, тогда ranknum будет 2,2,3 для типа волос «Брюнетка».

Каково решение этого вопроса?

+1

Какие СУБД вы используете? –

+0

Существует решение для этого по адресу http://stackoverflow.com/questions/3823939/, если вы не используете новые SQL-серверы. –

ответ

16

Если вы используете SQL Server 2005 или более поздней версии, вы можете использовать функции ранжирования и КТР для достижения этой цели:

;WITH HairColors AS 
(SELECT id, name, hair, score, 
     ROW_NUMBER() OVER(PARTITION BY hair ORDER BY score DESC) as 'RowNum' 
) 
SELECT id, name, hair, score 
FROM HairColors 
WHERE RowNum <= 3 

Этот КТР будет «раздела» Ваши данные по значению hair колонки , и каждый раздел затем упорядочивается по счету (убывает) и получает номер строки; самый высокий балл для каждой перегородки - 1, затем 2 и т. д.

Итак, если вы хотите ТОП-3 каждой группы, выберите только те строки из CTE, которые имеют RowNum из 3 или менее (1, 2, 3) -> Вот и все!

+0

ROW_NUMBER() OVER (PARTITION BY OR ORDER BY score DESC) как «RowNum»), скобка в этой строке не сбалансирована. совместимо с db2 sql grammar? – zinking

+0

@zinking: спасибо - там было слишком много закрывающих парнеров .. исправлено это! Я не знаю, что делать, если DB2 поддерживает это (не знаю DB2 достаточно), но это определенно стандартная конструкция ANSI/ISO SQL - не придуманная Microsoft функция :-) –

+1

Дерьмо, это только что сделало мой день! Какое введение в CTE! –

0

Способ, которым алгоритм подходит к рангу, состоит в том, чтобы подсчитать количество строк в кросс-продукте со счетом, равным или большим, чем соответствующая девушка, чтобы создать ранг. Таким образом, в случае проблем вы говорите, сетка Сары будет выглядеть

a.name | a.score | b.name | b.score 
-------+---------+---------+-------- 
Sarah | 9  | Sarah | 9 
Sarah | 9  | Deborah | 9 

и аналогично для Деборы, поэтому обе девочки получают звание 2 здесь.

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

Вместо сравнения, отличного от или равного, используйте строго большее сравнение, чтобы подсчитать количество девушек, которые строго лучше. Затем добавьте один к этому, и у вас есть свой ранг (который будет иметь дело со связями по мере необходимости). Таким образом, внутренний выбор будет следующим:

SELECT a.id, COUNT(*) + 1 AS ranknum 
FROM girl AS a 
    INNER JOIN girl AS b ON (a.hair = b.hair) AND (a.score < b.score) 
GROUP BY a.id 
HAVING COUNT(*) <= 3 

Может ли кто-нибудь увидеть какие-либо проблемы с этим подходом, которые ускользнули от моего уведомления?

+0

Не работает ли это в квадратичное время? – b0fh

0

Используйте это соединение выбора, который обрабатывает проблемы OP правильно

SELECT g.* FROM girls as g 
WHERE g.score > IFNULL((SELECT g2.score FROM girls as g2 
       WHERE g.hair=g2.hair ORDER BY g2.score DESC LIMIT 3,1), 0) 

Обратите внимание, что вам нужно использовать IFNULL здесь, чтобы обрабатывать случай, когда таблица девочки имеет меньше строк для некоторых типов волос то мы хотим см. в sql-ответе (в случае OP это 3 элемента).

Смежные вопросы