Istnieje wiele sposobów, aby to osiągnąć za pomocą istniejących funkcji.Możesz użyć istniejącego funkcje okien first_value()
i last_value()
, w połączeniu z DISTINCT
lub DISTINCT ON
aby uzyskać to bez sprzężeń i podzapytań:
SELECT DISTINCT ON (userid)
userid
, last_value(rank) OVER w
- first_value(rank) OVER w AS rank_delta
FROM rankings
WINDOW w AS (PARTITION BY userid ORDER BY ts
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING);
Zwróć uwagę na niestandardowe ramki dla funkcji okna !
Możesz też użyć podstawowych funkcji agregujących w podzapytaniu i JOIN:
SELECT userid, r2.rank - r1.rank AS rank_delta
FROM (
SELECT userid
, min(ts) AS first_ts
, max(ts) AS last_ts
FROM rankings
GROUP BY 1
) sub
JOIN rankings r1 USING (userid)
JOIN rankings r2 USING (userid)
WHERE r1.ts = first_ts
AND r2.ts = last_ts;
Zakładając unikalny (userid, rank)
lub Twoje wymagania byłyby niejednoznaczne.
Shichinin nie samuraj
Na żądanie w komentarzach, to samo tylko dla ostatnich siedmiu wierszy na identyfikator użytkownika (lub tyle, ile można znaleźć, jeśli jest ich mniej):
Znowu jeden z wielu możliwych sposobów. Ale uważam, że jest to jeden z najkrótszych:
SELECT DISTINCT ON (userid)
userid
, first_value(rank) OVER w
- last_value(rank) OVER w AS rank_delta
FROM rankings
WINDOW w AS (PARTITION BY userid ORDER BY ts DESC
ROWS BETWEEN CURRENT ROW AND 7 FOLLOWING)
ORDER BY userid, ts DESC;
Zwróć uwagę na odwróconą kolejność sortowania. Pierwszy wiersz to „najnowszy” wpis. Obejmuję ramkę (maks.) 7 wierszy i wybieram tylko wyniki dla najnowszego wpisu z DISTINCT ON
.