luki-i-wyspy rzeczywiście problem.
Zakładając:
- „Serie” nie są przerywane rzędami innych graczy.
- Wszystkie kolumny są zdefiniowane
NOT NULL
. (W przeciwnym razie musisz zrobić więcej.)
Powinno to być najprostsze i najszybsze, ponieważ wymaga tylko dwóch szybkich row_number()
funkcje okien
:
SELECT DISTINCT ON (player_id)
player_id, count(*) AS seq_len, min(ts) AS time_began
FROM (
SELECT player_id, points, ts
, row_number() OVER (PARTITION BY player_id ORDER BY ts)
- row_number() OVER (PARTITION BY player_id, points ORDER BY ts) AS grp
FROM tbl
) sub
WHERE points = 100
GROUP BY player_id, grp -- omit "points" after WHERE points = 100
ORDER BY player_id, seq_len DESC, time_began DESC;
db<>fiddle tutaj
Używanie nazwy kolumny ts
zamiast time
, czyli słowo zastrzeżone
w standardowym SQL. Jest to dozwolone w Postgresie, ale z ograniczeniami i nadal złym pomysłem jest używanie go jako identyfikatora.
"Sztuczka" polega na odjęciu numerów wierszy tak, aby kolejne wiersze należały do tej samej grupy (grp
) za (player_id, points)
. Wtedy filtruj te z 100 punktami, agreguj według grupy i zwracaj tylko najdłuższy, najnowszy wynik na gracza.
Podstawowe wyjaśnienie techniki:
Możemy użyć GROUP BY
i DISTINCT ON
w tym samym SELECT
, GROUP BY
jest stosowany przed DISTINCT ON
. Rozważ sekwencję zdarzeń w SELECT
zapytanie:
Informacje o DISTINCT ON
: