Как выбрать верхнюю и нижнюю записи для каждой группы? - PullRequest
1 голос
/ 07 апреля 2009

Скажем, у меня есть таблица в базе данных (SQL Server 2008) с данными, подобными этим (но намного, намного больше):

| ID | SCORE | GROUP |
-----------------------
| 10 |     1 | A     |
| 6  |     2 | A     |
| 3  |     3 | A     |
|----|-------|-------|
| 8  |     5 | B     |
|----|-------|-------|
| 4  |     1 | C     |
| 9  |     3 | C     |
| 2  |     4 | C     |
| 7  |     4 | C     |
|----|-------|-------|
| 12 |     3 | D     |
| 1  |     3 | D     |
| 11 |     4 | D     |
| 5  |     6 | D     |

Я бы хотел получить ID верхней и нижней записей для каждого GROUP, где записи для каждой группы упорядочены по SCORE (и дополнительно ID), например так:

| GROUP | MIN_ID | MAX_ID  |
----------------------------
| A     | 10     | 3       |
| B     | 8      | 8       |
| C     | 4      | 7       |
| D     | 1      | 5       |

Вопрос: как мне этого добиться?

До сих пор я пытался найти решения, основанные на функции RANK(), но мне не удалось найти запрос, который одновременно выдает правильные результаты и имеет неопределенную эффективность или удобство обслуживания.


Примечания:

Пример упрощен. Моя «таблица» на самом деле является выводом уже сложного запроса, к которому я хочу добавить заключительные этапы. Я бы предпочел выбрать из таблицы только один раз.

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

ID не в удобном порядке.

Ответы [ 4 ]

5 голосов
/ 07 апреля 2009

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

WITH q AS
        (
        SELECT  m.*,
                ROW_NUMBER() OVER (PARTITION BY Group ORDER BY Score) AS rn_asc,
                ROW_NUMBER() OVER (PARTITION BY Group ORDER BY Score DESC) AS rn_desc
        FROM    mytable m
        )
SELECT  *
FROM    q
WHERE   rn_asc BETWEEN 1 AND 10
        OR rn_desc BETWEEN 1 AND 10
3 голосов
/ 07 апреля 2009
DECLARE @YourTable TABLE (ID INTEGER, Score INTEGER, [Group] VARCHAR(1))
INSERT INTO @YourTable VALUES (10, 1, 'A')
INSERT INTO @YourTable VALUES (6 , 2, 'A')
INSERT INTO @YourTable VALUES (3 , 3, 'A')
INSERT INTO @YourTable VALUES (8 , 5, 'B')
INSERT INTO @YourTable VALUES (4 , 1, 'C')
INSERT INTO @YourTable VALUES (9 , 3, 'C')
INSERT INTO @YourTable VALUES (2 , 4, 'C')
INSERT INTO @YourTable VALUES (7 , 4, 'C')
INSERT INTO @YourTable VALUES (12, 3, 'D')
INSERT INTO @YourTable VALUES (1 , 3, 'D')
INSERT INTO @YourTable VALUES (11, 4, 'D')
INSERT INTO @YourTable VALUES (5 , 6, 'D')  

SELECT [Group], MIN([Min_ID]), MAX([Max_ID])
FROM (
  SELECT [score].[Group], [Min_ID] = [min].ID, [Max_ID] = [max].ID
  FROM (
    SELECT [Group], [Min_Score] = MIN(Score), [Max_Score] = MAX(Score)
    FROM @YourTable
    GROUP BY [GROUP]) score
    INNER JOIN @YourTable [min] ON [min].[Group] = [score].[Group] AND [min].[Score] = [score].[Min_Score]
    INNER JOIN @YourTable [max] ON [max].[Group] = [score].[Group] AND [max].[Score] = [score].[Max_Score] 
  ) yourtable
GROUP BY [yourtable].[Group]
1 голос
/ 07 апреля 2009

Любое решение также потребует хороших показателей по группе и баллу, но включает идентификатор

SELECT
    foo.[Group],
    m1.ID AS Min_ID,
    m2.ID AS Max_ID
FROM
    (
    SELECT
       [Group], MIN(Score) AS MinScore, MAX(Score) AS MaxScore
    FROM
       mytable
    GROUP BY
       [Group]
    ) foo
    JOIN
    mytable m1 ON foo.[Group] = m1.[Group] AND foo.MinScore = m1.Score
    JOIN
    mytable m2 ON foo.[Group] = m2.[Group] AND foo.MaxScore = m2.Score

Однако в ваших данных образца это также работает, потому что ID и оценка выровнены по порядку:

SELECT
    [Group],
    MIN(ID) AS Min_ID,
    MAX(ID) AS Max_ID
FROM
    mytable
GROUP BY
    [Group]
0 голосов
/ 07 апреля 2009

Вы можете использовать подзапрос (SELECT TOP 1) ...

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