выбор первых N строк для каждой группы в таблице - PullRequest
13 голосов
/ 22 октября 2010

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

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

Мне нужен такой набор результатовчто для каждого цвета волос, дайте мне 3 лучших имени бомбардира-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), что и Сара, то ранкнум будет 2,2,3 для волос типа "Брюнетка".

Какое решение для этого?

Ответы [ 3 ]

17 голосов
/ 22 октября 2010

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

;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

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

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

0 голосов
/ 23 марта 2014

Используйте этот составной выбор, который правильно обрабатывает проблему 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 для обработки случая, когда в таблице girls меньше строк для некоторого типа hair тогда мы хотим увидеть ответ sql (в случае OP это 3 пункта).

0 голосов
/ 22 октября 2010

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

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

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

...