A gaps-and-islands problema davvero.
Supponendo:
- Le "serie" non vengono interrotte dalle righe di altri giocatori.
- Tutte le colonne sono definite
NOT NULL
. (Altrimenti devi fare di più.)
Questo dovrebbe essere il più semplice e veloce in quanto richiede solo due veloci row_number()
funzioni della finestra
:
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<>violino qui
Usando il nome della colonna ts
invece di time
, che è una parola riservata
nell'SQL standard. È consentito in Postgres, ma con limitazioni ed è comunque una cattiva idea usarlo come identificatore.
Il "trucco" consiste nel sottrarre i numeri di riga in modo che le righe consecutive rientrino nello stesso gruppo (grp
) per (player_id, points)
. Allora filtra quelli con 100 punti, aggregati per gruppo e restituisci solo il risultato più lungo e più recente per giocatore.
Spiegazione di base per la tecnica:
Possiamo usare GROUP BY
e DISTINCT ON
nello stesso SELECT
, GROUP BY
viene applicato prima DISTINCT ON
. Considera la sequenza di eventi in un SELECT
domanda:
Informazioni su DISTINCT ON
: