Stół:
UserId, Value, Date.
Chcę uzyskać identyfikator użytkownika, wartość maks. (Data) dla każdego identyfikatora użytkownika. Oznacza to wartość dla każdego identyfikatora użytkownika, który ma najnowszą datę. Czy jest na to sposób po prostu w SQL? (Najlepiej Oracle)
Aktualizacja: Przepraszamy za wszelkie niejasności: muszę uzyskać WSZYSTKIE identyfikatory użytkowników. Ale dla każdego identyfikatora użytkownika tylko ten wiersz, w którym użytkownik ma najnowszą datę.
sql
oracle
greatest-n-per-group
Umang
źródło
źródło
Odpowiedzi:
Spowoduje to pobranie wszystkich wierszy, dla których wartość kolumny my_date jest równa maksymalnej wartości my_date dla tego identyfikatora użytkownika. Może to pobrać wiele wierszy dla identyfikatora użytkownika, w którym maksymalna data dotyczy wielu wierszy.
„Funkcje analityczne rock”
Edycja: W odniesieniu do pierwszego komentarza ...
„korzystanie z zapytań analitycznych i samozłączenie przeczy celowi zapytań analitycznych”
W tym kodzie nie ma możliwości samodzielnego przyłączenia się. Zamiast tego w wyniku widoku wbudowanego znajduje się predykat, który zawiera funkcję analityczną - zupełnie inna sprawa i całkowicie standardowa praktyka.
„Domyślne okno w Oracle jest od pierwszego wiersza partycji do bieżącego”
Klauzula okienkowa ma zastosowanie wyłącznie w przypadku zamówienia według klauzuli. Bez kolejności według klauzuli domyślnie nie jest stosowana żadna klauzula okienkowa i żadnej nie można wyraźnie określić.
Kod działa.
źródło
MAX(...) OVER (...)
możesz także użyćROW_NUMBER() OVER (...)
(dla największej grupy na grupę) lubRANK() OVER (...)
(dla największej grupy na grupę).Widzę, że wiele osób korzysta z podkwerend lub funkcji specyficznych dla dostawców, aby to zrobić, ale często wykonuję tego rodzaju zapytania bez podkwerend w następujący sposób. Używa zwykłego, standardowego SQL, więc powinien działać w dowolnej marce RDBMS.
Innymi słowy: pobierz wiersz z miejsca, w
t1
którym nie istnieje żaden inny wiersz z tym samymUserId
i większej dacie.(Umieszczam identyfikator „Data” w ogranicznikach, ponieważ jest to słowo zastrzeżone SQL).
W przypadku, gdy
t1."Date" = t2."Date"
pojawi się podwojenie. Zwykle tabele mająauto_inc(seq)
klucz, npid
. Aby uniknąć podwojenia, można zastosować:Ponownie skomentuj @Farhan:
Oto bardziej szczegółowe wyjaśnienie:
Zewnętrzna próbach przyłączenia do przyłączenia
t1
sięt2
. Domyślniet1
zwracane są wszystkie wyniki , a jeśli istnieje dopasowaniet2
, jest ono również zwracane. Jeśli nie ma dopasowaniat2
dla danego wierszat1
, wówczas zapytanie nadal zwraca wierszt1
i używaNULL
jako symbolu zastępczego dla wszystkicht2
kolumn. Tak właśnie działają sprzężenia zewnętrzne.Sztuczka w tym zapytaniu polega na zaprojektowaniu warunków dopasowania łączenia, które
t2
muszą być takie sameuserid
i większedate
. Pomysł jest, jeśli wiersz istniejet2
, że ma większądate
, wówczas wierszt1
to jest porównywana nie może być największymdate
na touserid
. Ale jeśli nie ma dopasowania - tj. Jeśli nie istnieje żaden wierszt2
z wartością większądate
niż wiersz int1
- wiemy, że wiersz wt1
był wierszem o największymdate
dla danegouserid
.W tych przypadkach (gdy nie ma dopasowania), kolumny
t2
będąNULL
- nawet kolumny określone w warunku łączenia. Dlatego używamyWHERE t2.UserId IS NULL
, ponieważ szukamy przypadków, w których nie znaleziono żadnego wiersza z większymdate
dla podanegouserid
.źródło
źródło
Nie znam dokładnych nazw kolumn, ale byłoby to mniej więcej tak:
źródło
Nie będąc w pracy, nie mam pod ręką Oracle, ale wydaje mi się, że pamiętam, że Oracle pozwala na dopasowanie wielu kolumn w klauzuli IN, co powinno przynajmniej unikać opcji wykorzystujących skorelowane podzapytanie, które rzadko jest dobrym pomysł.
Być może coś takiego (nie pamiętam, czy lista kolumn powinna być nawiasowana, czy nie):
EDYCJA: Właśnie wypróbowałem to naprawdę:
Więc to działa, chociaż niektóre nowiutkie rzeczy wspomniane gdzie indziej mogą być bardziej wydajne.
źródło
Wiem, że poprosiłeś o Oracle, ale w SQL 2005 używamy teraz tego:
źródło
Nie mam Oracle, aby to przetestować, ale najbardziej wydajnym rozwiązaniem jest użycie zapytań analitycznych. Powinno to wyglądać mniej więcej tak:
Podejrzewam, że możesz pozbyć się zewnętrznego zapytania i wyróżnić wewnętrzne, ale nie jestem pewien. Tymczasem wiem, że to działa.
Jeśli chcesz dowiedzieć się więcej na temat zapytań analitycznych, sugeruję przeczytanie http://www.orafaq.com/node/55 i
http://www.akadia.com/services/ora_analytic_functions.html. Oto krótkie podsumowanie.Kwerendy analityczne pod maską posortuj cały zestaw danych, a następnie przetworz go sekwencyjnie. Podczas przetwarzania partycjonujesz zestaw danych według określonych kryteriów, a następnie dla każdego wiersza patrzy na niektóre okna (domyślnie na pierwszą wartość w partycji na bieżący wiersz - ta domyślna jest również najbardziej wydajna) i możesz obliczyć wartości przy użyciu liczba funkcji analitycznych (lista, która jest bardzo podobna do funkcji agregujących).
W tym przypadku jest to, co robi wewnętrzne zapytanie. Cały zestaw danych jest sortowany według UserId, a następnie Date DESC. Następnie przetwarza go w jednym przejściu. Dla każdego wiersza zwracany jest identyfikator użytkownika i pierwsza data widziana dla tego identyfikatora użytkownika (ponieważ daty są sortowane DESC, to jest data maksymalna). To daje odpowiedź ze zduplikowanymi wierszami. Następnie zewnętrzne DISTINCT wyciskają duplikaty.
To nie jest szczególnie spektakularny przykład zapytań analitycznych. W przypadku znacznie większej wygranej należy wziąć pod uwagę tabelę wpływów finansowych i obliczać dla każdego użytkownika i pokwitowania bieżącą sumę zapłaconych kwot. Kwerendy analityczne rozwiązują to skutecznie. Inne rozwiązania są mniej wydajne. Dlatego są częścią standardu SQL 2003. (Niestety Postgres nie ma ich jeszcze. Grrr ...)
źródło
Czy klauzula QUALIFY nie byłaby jednocześnie najprostsza i najlepsza?
Dla kontekstu, na Teradata tutaj, przyzwoity test tego rozmiaru jest uruchamiany w 17s w tej wersji QUALIFY, aw 23s w „widoku wbudowanym” / rozwiązaniu Aldridge nr 1.
źródło
rank()
funkcję w sytuacjach, w których istnieją więzi. Możesz skończyć z więcej niż jednymrank=1
. Lepiej użyć,row_number()
jeśli naprawdę chcesz zwrócić tylko jeden rekord.QUALIFY
klauzula jest specyficzna dla Teradata. W Oracle (przynajmniej) musisz zagnieździć swoje zapytanie i filtrować, używającWHERE
klauzuli na zawijającej instrukcji select (która prawdopodobnie uderzy w wydajność, jak sądzę).W
Oracle 12c+
można użyć zapytań Top n wraz z funkcją analityczną,rank
aby osiągnąć to bardzo zwięźle bez podkwerend:Powyższe zwraca wszystkie wiersze z maksymalną datą_moje na użytkownika.
Jeśli chcesz tylko jeden wiersz z max daty, a następnie zastąpić
rank
zrow_number
:źródło
Użyj,
ROW_NUMBER()
aby przypisać unikalny ranking malejącejDate
dla każdegoUserId
, a następnie filtruj do pierwszego wiersza dla każdegoUserId
(tj.ROW_NUMBER
= 1).źródło
W PostgreSQL 8.4 lub nowszym możesz użyć tego:
źródło
myślę, że powinieneś zrobić ten wariant do poprzedniego zapytania:
źródło
źródło
Po prostu musiałem napisać „na żywo” przykład w pracy :)
Ta obsługuje wiele wartości dla UserId w tym samym dniu.
Kolumny: identyfikator użytkownika, wartość, data
Możesz użyć FIRST_VALUE zamiast MAX i sprawdzić w planie wyjaśniania. Nie miałem czasu się z tym bawić.
Oczywiście, jeśli przeszukujesz ogromne tabele, prawdopodobnie lepiej jest użyć PEŁNYCH wskazówek w zapytaniu.
źródło
źródło
Myślę, że coś takiego. (Wybacz mi błędy w składni; w tym momencie jestem przyzwyczajony do używania HQL!)
EDYCJA: Również źle odczytałem pytanie! Poprawiono zapytanie ...
źródło
(T-SQL) Najpierw pobierz wszystkich użytkowników i ich maksymalną datę. Dołącz do tabeli, aby znaleźć odpowiednie wartości dla użytkowników w maksymalnych terminach.
wyniki:
źródło
Odpowiedzią jest tylko Oracle. Oto nieco bardziej wyrafinowana odpowiedź we wszystkich SQL:
Kto ma najlepszy ogólny wynik pracy domowej (maksymalna suma punktów pracy domowej)?
I trudniejszy przykład, który wymaga wyjaśnienia, dla którego nie mam czasu atm:
Podaj książkę (ISBN i tytuł), która jest najbardziej popularna w 2008 r., Tj. Która jest najczęściej wypożyczana w 2008 r.
Mam nadzieję, że to pomoże (każdemu) .. :)
Pozdrawiam, Guus
źródło
Zakładając, że data jest unikalna dla danego ID użytkownika, oto niektóre TSQL:
źródło
Jestem spóźniony na imprezę, ale następujący hack prześcignie zarówno skorelowane podkwerendy, jak i dowolną funkcję analityczną, ale ma jedno ograniczenie: wartości muszą zostać przekonwertowane na ciągi. Działa to dla dat, liczb i innych ciągów. Kod nie wygląda dobrze, ale profil wykonania jest świetny.
Powodem, dla którego ten kod działa tak dobrze, jest to, że wystarczy zeskanować tabelę tylko raz. Nie wymaga żadnych indeksów, a co najważniejsze, nie musi sortować tabeli, co robi większość funkcji analitycznych. Indeksy będą pomocne, jeśli będziesz musiał przefiltrować wynik dla pojedynczego identyfikatora użytkownika.
źródło
IMHO to działa. HTH
źródło
Myślę, że to powinno działać?
źródło
Najpierw źle odczytałem pytanie, podążając za najlepszą odpowiedzią, oto kompletny przykład z poprawnymi wynikami:
-
-
źródło
To również zajmie się duplikatami (zwróci jeden wiersz dla każdego identyfikatora użytkownika):
źródło
Właśnie to przetestowałem i wydaje się, że działa na tabeli rejestrowania
źródło
Powinno to być tak proste, jak:
źródło
Rozwiązanie dla MySQL, które nie ma koncepcji partycji KEEP, DENSE_RANK.
Odniesienie: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
źródło
Jeśli korzystasz z Postgres, możesz użyć
array_agg
likeNie znam Oracle. Właśnie to wymyśliłem
Oba zapytania zwracają takie same wyniki, jak zaakceptowana odpowiedź. Zobacz SQLFiddles:
źródło
Jeśli (identyfikator użytkownika, data) jest unikalny, tzn. Data nie pojawia się dwukrotnie dla tego samego użytkownika, wówczas:
źródło
źródło