2012-04-27 16 views
9

Moja tabela:Oracle SQL - Funkcje analityczne PONAD GRUPY?

ID NUM VAL 
1 1  Hello 
1 2  Goodbye 
2 2  Hey 
2 4  What's up? 
3 5  See you 

Jeśli chcę wrócić liczbę max dla każdego identyfikatora, to naprawdę ładne i czyste:

SELECT MAX(NUM) FROM table GROUP BY (ID) 

Ale co, jeśli chcę, aby pobrać wartość powiązaną z max każdego numeru dla każdego ID?

Dlaczego nie mogę zrobić:

SELECT MAX(NUM) OVER (ORDER BY NUM) FROM table GROUP BY (ID) 

Dlaczego jest to błąd? Chciałbym, aby to było wybrane pogrupowane według ID, a nie partycjonowanie osobno dla każdego okna ...

EDYCJA: Błąd "nie jest wyrażeniem GROUP BY".

+1

Nie możemy zobaczyć swój komunikat o błędzie stąd. – RBarryYoung

+0

Czy możesz pokazać wynik, którego oczekujesz, opis nie miał dla mnie sensu ... 'Chcę pobrać wartość związaną z maksymalną liczbą każdego numeru ID'. Nie jestem również pewien, jaka różnica ORDER BY spowodowałaby funkcję MAX() ... – MatBailie

+0

Brak dokładnej odpowiedzi na pytanie, ale dobre wprowadzenie na temat oracle Funkcje analityczne można znaleźć na stronie [orafaq] (http: // www.orafaq.com/node/55). Post pomaga zrozumieć pojęcia za pomocą prostych przykładów. –

Odpowiedz

13

Można chyba użyć MAX() KEEP(DENSE_RANK LAST...) funkcję:

with sample_data as (
    select 1 id, 1 num, 'Hello' val from dual union all 
    select 1 id, 2 num, 'Goodbye' val from dual union all 
    select 2 id, 2 num, 'Hey' val from dual union all 
    select 2 id, 4 num, 'What''s up?' val from dual union all 
    select 3 id, 5 num, 'See you' val from dual) 
select id, max(num), max(val) keep (dense_rank last order by num) 
from sample_data 
group by id; 
+1

+1 Wow, ta odpowiedź jest dobra. Czy nie wiedzieli, że istnieje funkcja KEEP dla funkcji okienkowania, a może jest zastrzeżona dla Oracle, niemniej jest to eleganckie rozwiązanie. Ta odpowiedź działa na większości baz danych http://stackoverflow.com/a/10359164 Oracle: http://www.sqlfiddle.com/#!4/a9a07/19 Sql Server: http://www.sqlfiddle.com/ #! 3/a9a07/1 Postgresql: http://www.sqlfiddle.com/#!1/a9a07/6 –

+0

Uwielbiam to, jak ciągle znajduję pytania o Oracle za pomocą kwerendy i bez wyjaśnienia, co robi diabeł i jak to działa. Bardzo pomocne w zastosowaniu rozwiązania w innych sytuacjach. – jpmc26

3

Jeśli szukasz, aby uzyskać wiersze które zawierają wartości z MAX(num) GROUP BY id, to wydaje się być wspólny wzór ...

WITH 
    sequenced_data 
AS 
(
    SELECT 
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY num DESC) AS sequence_id, 
    * 
    FROM 
    yourTable 
) 
SELECT 
    * 
FROM 
    sequenced_data 
WHERE 
    sequence_id = 1 


EDIT

ja nie wiem, czy Teradata pozwoli, ale logika wydaje się mieć sens ...

SELECT 
    * 
FROM 
    yourTable 
WHERE 
    num = MAX(num) OVER (PARTITION BY id) 

A może ...

SELECT 
    * 
FROM 
(
    SELECT 
    *, 
    MAX(num) OVER (PARTITION BY id) AS max_num_by_id 
    FROM 
    yourTable 
) 
    AS sub_query 
WHERE 
    num = max_num_by_id 

Jest nieco różni się od mojej poprzedniej odpowiedzi; jeśli wiele rekordów jest powiązanych z tymi samymi MAX(num), to zwróci je wszystkie, druga odpowiedź tylko zwróci jeden.


EDIT

W proponowanej SQL błąd odnosi się do faktu, że klauzula OVER() nie zawiera pola w GROUP BY. To tak, jakby starając się to zrobić ...

SELECT id, num FROM yourTable GROUP BY id 

num jest nieważne, ponieważ nie może być wiele wartości w tej dziedzinie za każdy wiersz zwrócony (z wierszy zwracanych jest określona przez GROUP BY id).

W ten sam sposób nie można umieścić num w klauzuli OVER().

SELECT 

    id, 

    MAX(num),    <-- Valid as it is an aggregate 

    MAX(num)     <-- still valid 
    OVER(PARTITION BY id), <-- Also valid, as id is in the GROUP BY 

    MAX(num)     <-- still valid 
    OVER(PARTITION BY num) <-- Not valid, as num is not in the GROUP BY 

FROM 
    yourTable 
GROUP BY 
    id 


Zobacz to pytanie, gdy nie można określić coś w klauzuli OVER() i odpowiedzi pokazujący kiedy (chyba) można: over-partition-by-question

+1

Szybka podróż do [http://sqlfiddle.com] (http://www.sqlfiddle.com/#!4/a9a07/23) może sprawdzić, czy to nie działa: 'wybierz id, val z twojegoTable gdzie num = max (num) over (partycja według id) '. Byłoby miło, gdyby działało, bardzo zwięźle. –

4

Podczas korzystania funkcja okienkowania, nie musisz już używać GROUP BY, wystarczy:

select id, 
    max(num) over(partition by id) 
from x 

Właściwie można uzyskać wynik bez używania funkcji okienkowy:

select * 
from x 
where (id,num) in 
    (
    select id, max(num) 
    from x 
    group by id 
) 

wyjściowa:

ID NUM VAL 
1 2 Goodbye 
2 4 What's up 
3 5 SEE YOU 

http://www.sqlfiddle.com/#!4/a9a07/7


Jeśli chcesz użyć funkcji okienkowy, możesz to zrobić:

select id, val, 
    case when num = max(num) over(partition by id) then 
     1 
    else 
     0 
    end as to_select 
from x 
where to_select = 1 

Albo to:

select id, val 
from x 
where num = max(num) over(partition by id) 

Ale ponieważ nie wolno ci zrobić, trzeba to zrobić:

with list as 
(
    select id, val, 
    case when num = max(num) over(partition by id) then 
     1 
    else 
     0 
    end as to_select 
    from x 
) 
select * 
from list 
where to_select = 1 

http://www.sqlfiddle.com/#!4/a9a07/19

+0

Co osiąga "ZAMÓWIENIE PRZEZNACZONEJ"? 'MAX()' z '1,2,3' jest tym samym, co' MAX() 'z' 2,3,1' ... Dla 'MAX()', z pewnością tylko "PARTYCJA BY" ma sens? – MatBailie

+1

Tak, to prawda, tylko PARTYCJA BY ma sens w MAX. pierwszy kod (MAX OVER ORDER BY) kod w mojej odpowiedzi został po prostu szybko skopiowany z pytania OP. ORDER BY dla funkcji okienkowania ma sens tylko w przypadku sumy całkowitej, np. SUMA –

+0

Cóż, ROW_NUMBER(), RANK(), etc, * (ponieważ zwracają różne wartości w zależności od pozycji/kolejności) * ale nie MIN(), MAX(), SUM()? – MatBailie