2010-08-16 15 views
102

Próbuję listy najnowszy cel (MAX czas odjazdu) dla każdego pociągu w tabeli, for example:GROUP BY z MAX (data)

Train Dest  Time 
1  HK  10:00 
1  SH  12:00 
1  SZ  14:00 
2  HK  13:00 
2  SH  09:00 
2  SZ  07:00 

pożądany wynik powinien być:

Train Dest  Time 
1  SZ  14:00 
2  HK  13:00 

próbowałem, używając

SELECT Train, Dest, MAX(Time) 
FROM TrainTable 
GROUP BY Train 

przez dostałem "ORA-00979 a nie grupa przez ekspresję" błąd mówiąc, że Muszę dodać "Dest" do mojej grupy przez oświadczenie. Ale na pewno nie tego chcę ...

Czy można to zrobić w jednym wierszu kodu SQL?

+2

Dla tych którzy zastanawiają się, najczystsze "zwykłe sql" rozwiązanie jest [jeden przez Joe] (http://stackoverflow.com/a/14841015/632951). Drugą nagrodę otrzymuje [Claudio] (http://stackoverflow.com/a/24370489/632951). – Pacerier

Odpowiedz

109

Nie można dołączyć kolumn niezagregowanych do zestawu wyników, które nie są zgrupowane. Jeśli pociąg ma tylko jedno miejsce docelowe, po prostu dodaj kolumnę docelową do klauzuli group by, w przeciwnym razie musisz ponownie przemyśleć zapytanie.

Spróbuj:

SELECT t.Train, t.Dest, r.MaxTime 
FROM (
     SELECT Train, MAX(Time) as MaxTime 
     FROM TrainTable 
     GROUP BY Train 
) r 
INNER JOIN TrainTable t 
ON t.Train = r.Train AND t.Time = r.MaxTime 
+7

Ostrożnie , to nie zadziała, jeśli istnieją "krawaty" dla max (czas), ponieważ będziesz otrzymywać wiele wierszy. Musisz dodać 'group by train, dest' tuż za powyższym zapytaniem. – Pacerier

118
SELECT train, dest, time FROM ( 
    SELECT train, dest, time, 
    RANK() OVER (PARTITION BY train ORDER BY time DESC) dest_rank 
    FROM traintable 
) where dest_rank = 1 
+0

Thx Thilo :-) Rzeczywiście twoja odpowiedź jest również poprawna. Ale ponieważ mogę tylko przyjąć 1 odpowiedź, wybrałem Olivera, ponieważ najpierw spróbowałem jego odpowiedzi. – Aries

+5

@Aries - odpowiedź Thilo jest lepsza od odpowiedzi Olivera, ponieważ odpowiedź Thilo będzie miała mniejszą moc wejścia/wyjścia. Funkcja analityczna pozwala SQLowi przetworzyć tabelę w jednym przebiegu, podczas gdy rozwiązanie Olivera wymaga wielu przejść. –

+3

tak, zgadzam się. Ta odpowiedź powinna być "poprawna". – Russell

8

Dopóki nie istnieją duplikaty (i pociągi mają tendencję do osiągnięcia tylko na jednej stacji na raz) ...

select Train, MAX(Time), 
     max(Dest) keep (DENSE_RANK LAST ORDER BY Time) max_keep 
from TrainTable 
GROUP BY Train; 
+1

"i pociągi mają tendencję do przyjazdu tylko na jednej stacji na raz" ... Nie jest to określone. – Pacerier

61

Oto przykład, który używa tylko Left przyłączyć i wierzę, że jest bardziej efektywne niż jakiekolwiek grupy metodą tam: ExchangeCore Blog

SELECT t1.* 
FROM TrainTable t1 LEFT JOIN TrainTable t2 
ON (t1.Train = t2.Train AND t1.Time < t2.Time) 
WHERE t2.Time IS NULL; 
+7

Podoba mi się to podejście, ponieważ używa tylko standardowego SQL i działa naprawdę dobrze i szybko. – GreenTurtle

+2

to jest najlepiej zoptymalizowana odpowiedź – diEcho

+0

Cudowne rozwiązanie. Znacznie lepiej niż skomplikowane klauzule grupowe miałem w głowie. Po prostu uwielbiam sprytnie eleganckie rzeczy takie jak to, dzięki! –

8

Anoth Rozwiązanie er:

select * from traintable 
where (train, time) in (select train, max(time) from traintable group by train); 
+1

Ostrożnie, to nie zadziała, jeśli istnieją "krawaty" dla max (czas), ponieważ będziesz otrzymywać wiele wierszy. Użyj tego zamiast: 'wybierz * z traintable gdzie (pociąg, czas) w (wybierz pociąg, max (czas) z traintable grupy pociągiem) grupa pociągiem, dest; ' – Pacerier

3

Wiem, że późno do partii, ale spróbuj tego ...

SELECT 
    `Train`, 
    `Dest`, 
    SUBSTRING_INDEX(GROUP_CONCAT(`Time` ORDER BY `Time` DESC), ",", 1) AS `Time` 
FROM TrainTable 
GROUP BY Train; 

Src: Group Concat Documentation

Edit: stałe składni SQL