A gaps-and-islands problème en effet.
En supposant :
- Les "séries" ne sont pas interrompues par des rangées d'autres joueurs.
- Toutes les colonnes sont définies
NOT NULL
. (Sinon, vous devez en faire plus.)
Cela devrait être le plus simple et le plus rapide car il ne nécessite que deux row_number()
fonctions de fenêtre
:
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<>violon ici
Utilisation du nom de colonne ts
au lieu de time
, qui est un mot réservé
en SQL standard. C'est autorisé dans Postgres, mais avec des limitations et c'est toujours une mauvaise idée de l'utiliser comme identifiant.
Le "truc" consiste à soustraire les numéros de ligne afin que les lignes consécutives tombent dans le même groupe (grp
) par (player_id, points)
. Alors filtrez ceux avec 100 points, regroupez par groupe et ne retournez que le résultat le plus long et le plus récent par joueur.
Explication de base de la technique :
Nous pouvons utiliser GROUP BY
et DISTINCT ON
dans le même SELECT
, GROUP BY
est appliqué avant DISTINCT ON
. Considérez la séquence d'événements dans un SELECT
requête :
À propos de DISTINCT ON
: