Mam do czynienia z tabelą Postgres (o nazwie „lives”), która zawiera rekordy z kolumnami dla datownika, usr_id, transaction_id i lives_remaining. Potrzebuję zapytania, które da mi ostatnią liczbę pozostałych żyć dla każdego identyfikatora usr_id
- Istnieje wielu użytkowników (różne usr_id)
- time_stamp nie jest unikalnym identyfikatorem: czasami zdarzenia użytkownika (jeden po wierszu w tabeli) będą miały miejsce z tym samym znacznikiem czasu.
- trans_id jest unikalny tylko dla bardzo małych przedziałów czasowych: w czasie się powtarza
- pozostałe_lives (dla danego użytkownika) mogą zarówno rosnąć, jak i spadać w czasie
przykład:
time_stamp | lives_remaining | usr_id | trans_id ----------------------------------------- 07:00 | 1 | 1 | 1 09:00 | 4 | 2 | 2 10:00 | 2 | 3 | 3 10:00 | 1 | 2 | 4 11:00 | 4 | 1 | 5 11:00 | 3 | 1 | 6 13:00 | 3 | 3 | 1
Ponieważ będę musiał uzyskać dostęp do innych kolumn wiersza z najnowszymi danymi dla każdego podanego identyfikatora usr_id, potrzebuję zapytania, które da następujący wynik:
time_stamp | lives_remaining | usr_id | trans_id ----------------------------------------- 11:00 | 3 | 1 | 6 10:00 | 1 | 2 | 4 13:00 | 3 | 3 | 1
Jak wspomniano, każdy usr_id może zyskać lub stracić życie, a czasami te zdarzenia z sygnaturą czasową występują tak blisko siebie, że mają ten sam znacznik czasu! Dlatego to zapytanie nie zadziała:
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp) AS max_timestamp
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp = b.time_stamp
Zamiast tego muszę użyć zarówno sygnatury czasowej (pierwszej), jak i trans_id (drugiej), aby zidentyfikować właściwy wiersz. Muszę również przekazać te informacje z podzapytania do głównego zapytania, które dostarczy dane dla innych kolumn odpowiednich wierszy. Oto zhakowane zapytanie, które zabrałem do pracy:
SELECT b.time_stamp,b.lives_remaining,b.usr_id,b.trans_id FROM
(SELECT usr_id, max(time_stamp || '*' || trans_id)
AS max_timestamp_transid
FROM lives GROUP BY usr_id ORDER BY usr_id) a
JOIN lives b ON a.max_timestamp_transid = b.time_stamp || '*' || b.trans_id
ORDER BY b.usr_id
Ok, więc to działa, ale mi się to nie podoba. Wymaga kwerendy w zapytaniu, samosprzężenia i wydaje mi się, że mogłoby to być znacznie prostsze, chwytając wiersz, który MAX uznał za mający największy znacznik czasu i trans_id. Tabela „żyje” ma dziesiątki milionów wierszy do przeanalizowania, dlatego chciałbym, aby to zapytanie było tak szybkie i wydajne, jak to tylko możliwe. W szczególności jestem nowy w RDBM i Postgresie, więc wiem, że muszę efektywnie wykorzystywać odpowiednie indeksy. Trochę zagubiłem się w optymalizacji.
Znalazłem podobną dyskusję tutaj . Czy mogę wykonać jakiś typ Postgres będący odpowiednikiem funkcji analitycznej Oracle?
Wszelkie porady dotyczące uzyskiwania dostępu do powiązanych informacji z kolumn używanych przez funkcję agregującą (taką jak MAX), tworzenia indeksów i tworzenia lepszych zapytań byłyby bardzo mile widziane!
PS Możesz użyć następujących, aby utworzyć moją przykładową sprawę:
create TABLE lives (time_stamp timestamp, lives_remaining integer,
usr_id integer, trans_id integer);
insert into lives values ('2000-01-01 07:00', 1, 1, 1);
insert into lives values ('2000-01-01 09:00', 4, 2, 2);
insert into lives values ('2000-01-01 10:00', 2, 3, 3);
insert into lives values ('2000-01-01 10:00', 1, 2, 4);
insert into lives values ('2000-01-01 11:00', 4, 1, 5);
insert into lives values ('2000-01-01 11:00', 3, 1, 6);
insert into lives values ('2000-01-01 13:00', 3, 3, 1);