2012-04-26 2 views
6

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

Вот что моя таблица может выглядеть следующим образом:

SCORES 
ID ROUND SCORE 
1  1  3 
1  2  6 
1  3  2 
2  1  10 
2  2  12 
3  1  6 

Мне нужно, чтобы вернуть счет, что каждый ID получил в последнем раунде. То есть, строка с максимальным (круглым), но не максимальным счетом.

OUTPUT: 
ID ROUND SCORE 
1 3  2 
2 2  12 
3 1  6 

Прямо сейчас у меня есть:

SELECT * FROM 
(SELECT id, round, 
CASE WHEN (MAX(round) OVER (PARTITION BY id)) = round THEN score ELSE NULL END score 
FROM 
SCORES 
where id in (1,2,3) 
) scorevals 
WHERE 
scorevals.round is not null; 

Это работает, но довольно неэффективно (я должен вручную отфильтровать все из этих строк, когда я должен просто быть в состоянии не захватить эти строки в на первом месте.)

Что я могу сделать для того, чтобы получить правильные значения?

ответ

4

Это также возможно без подзапроса:

SELECT DISTINCT 
     id 
     ,max(round) OVER (PARTITION BY id) AS round 
     ,first_value(score) OVER (PARTITION BY id ORDER BY round DESC) AS score 
FROM SCORES 
WHERE id IN (1,2,3) 
ORDER BY id; 

Возвращает именно то, что вы просили.
Важнейшим моментом является то, что DISTINCT применяется после функции окна.

SQL Fiddle.

Может быть быстрее, потому что он использует то же самое окно дважды:

SELECT DISTINCT 
     id 
     ,first_value(round) OVER (PARTITION BY id ORDER BY round DESC) AS round 
     ,first_value(score) OVER (PARTITION BY id ORDER BY round DESC) AS score 
FROM SCORES 
WHERE id IN (1,2,3) 
ORDER BY id; 

В противном случае делают то же самое.

+0

мне нравится; Очень чистый. – Jeremy

3

Вы находитесь на правильном пути, используя аналитические функции. Но вы, вероятно, хотите что-то подобное с функцией rank

SELECT * 
    FROM (SELECT a.*, 
       rank() over (partition by id order by round desc) rnk 
      FROM scores 
     WHERE id IN (1,2,3)) 
WHERE rnk = 1 

Если может быть связи (строки, которые имеют одинаковые id и round) вы можете захотеть использовать row_number аналитическую функцию вместо rank - что будет произвольно выберите один из двух связанных строк, чтобы иметь rnk из 1, а не возвращать оба, как rank.

Если вы хотите использовать MAX аналитическую функцию, вы также можете сделать что-то вроде

SELECT * 
    FROM (SELECT a.*, 
       MAX(round) OVER (partition by id) max_round 
      FROM scores 
     WHERE id IN (1,2,3)) 
WHERE round = max_round 
0

Для такого рода проблем, я предпочитаю использовать max...keep...dense_rank конструкцию:

select 
    id, 
    max(round) round, 
    max(score) keep (dense_rank last order by round) score 
from 
    tq84_scores 
group by 
    id; 

sql fiddle