2012-04-26 6 views
6

Ich habe es schwer, dies zu tun, ohne die gleiche Tabelle mindestens zweimal zu suchen, um die maximale Zeile zu greifen, und dann den Wert für diese Zeile zu greifen. Der fragliche Tisch ist ziemlich groß, das ist inakzeptabel.Return-Zeile mit dem maximalen Wert einer Spalte pro Gruppe

Hier ist, was meine Tabelle aussehen könnte:

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

Ich brauche die Partitur zurückzugeben, die jede ID in der letzten Runde bekam. Das heißt, die Zeile mit dem Maximum (rund), aber nicht die maximale Punktzahl.

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

Im Moment habe ich:

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; 

Dies funktioniert, aber ist ziemlich ineffizient (ich manuell alle diese Zeilen herausfiltern, wenn ich sollte nur in der Lage sein, nicht die Zeilen greifen in der erste Platz.)

Was kann ich tun, um die richtigen Werte zu erhalten?

Antwort

4

Dies auch ohne Unterabfrage möglich ist:

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; 

Returns genau das, was Sie gefragt.
Der entscheidende Punkt ist, dass DISTINCTnach Fensterfunktionen angewendet wird.

SQL Fiddle.

Vielleicht schneller, weil es verwendet das gleiche Fenster zweimal:

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; 

Ansonsten das gleiche zu tun.

+0

Ich mag es; sehr sauber. – Jeremy

3

Sie sind mit analytischen Funktionen auf dem richtigen Weg. Aber Sie wahrscheinlich so etwas wie dies mit der Funktion rank wollen

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 

Wenn kann es Verbindungen sein (Zeilen, die die gleiche id und round haben), können Sie die row_number analytische Funktion statt rank verwenden wollen - das wird willkürlich Wählen Sie eine der beiden gebundenen Reihen, um eine rnk von 1 zu erhalten, anstatt beide als rank zurückgeben zu müssen.

Wenn Sie die MAX analytische Funktion nutzen wollte, könnte man auch etwas tun, wie

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

Für diese Art von Problemen, neige ich dazu, das max...keep...dense_rank Konstrukt zu verwenden:

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

sql fiddle