A gaps-and-islands problem faktisk.
Forudsat:
- "Streaks" afbrydes ikke af rækker fra andre spillere.
- Alle kolonner er defineret
NOT NULL
. (Ellers skal du gøre mere.)
Dette burde være enklest og hurtigst, da det kun behøver to hurtige row_number()
vinduesfunktioner
:
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 her
Brug af kolonnenavnet ts
i stedet for time
, som er et reserveret ord
i standard SQL. Det er tilladt i Postgres, men med begrænsninger, og det er stadig en dårlig idé at bruge det som identifikator.
"Tricket" er at trække rækkenumre fra, så på hinanden følgende rækker falder i samme gruppe (grp
) pr. (player_id, points)
. Så filtrer dem med 100 point, samlet pr. gruppe og returner kun det længste, seneste resultat pr. spiller.
Grundlæggende forklaring på teknikken:
Vi kan bruge GROUP BY
og DISTINCT ON
i den samme SELECT
, GROUP BY
anvendes før DISTINCT ON
. Overvej rækkefølgen af hændelser i en SELECT
forespørgsel:
Om DISTINCT ON
: