Votre définition :
l'activité du groupe B a toujours lieu après l'activité du groupe A.
.. implique logiquement qu'il y a, par utilisateur, 0 ou 1 activité B après 1 ou plusieurs activités A. Jamais plus de 1 B d'activités consécutives.
Vous pouvez le faire fonctionner avec une fonction de fenêtre unique, DISTINCT ON
et CASE
, qui devrait être le moyen le plus rapide pour peu lignes par utilisateur (voir également ci-dessous) :
SELECT name
, CASE WHEN a2 LIKE 'B%' THEN a1 ELSE a2 END AS activity
, CASE WHEN a2 LIKE 'B%' THEN a2 END AS next_activity
FROM (
SELECT DISTINCT ON (name)
name
, lead(activity) OVER (PARTITION BY name ORDER BY time DESC) AS a1
, activity AS a2
FROM t
WHERE (activity LIKE 'A%' OR activity LIKE 'B%')
ORDER BY name, time DESC
) sub;
db<>jouez ici
Un CASE
SQL l'expression par défaut est NULL
si non ELSE
branche est ajoutée, j'ai donc été bref.
En supposant time
est défini NOT NULL
. Sinon, vous pouvez ajouter NULLS LAST
. Pourquoi ?
- Trier par colonne ASC, mais les valeurs NULL en premier ?
(activity LIKE 'A%' OR activity LIKE 'B%')
est plus verbeux que activity ~ '^[AB]'
, mais généralement plus rapide dans les anciennes versions de Postgres. À propos de la correspondance de modèles :
- Mise en correspondance de modèles avec LIKE, SIMILAR TO ou des expressions régulières dans PostgreSQL
Fonctions de fenêtre conditionnelle ?
C'est en fait possible . Vous pouvez combiner l'agrégat FILTER
clause avec le OVER
clause des fonctions de fenêtre. Cependant :
-
Le
FILTER
La clause elle-même ne peut fonctionner qu'avec les valeurs de la ligne actuelle. -
Plus important encore,
FILTER
n'est pas implémenté pour les fonctions authentiques pures commelead()
oulag()
(jusqu'à Postgres 13) - uniquement pour les fonctions d'agrégation.
Si vous essayez :
lead(activity) FILTER (WHERE activity LIKE 'A%') OVER () AS activity
Postgres vous dira :
FILTER is not implemented for non-aggregate window functions
À propos de FILTER
:
- Colonnes agrégées avec des filtres supplémentaires (distincts)
- Référence à la ligne actuelle dans la clause FILTER de la fonction de fenêtre
Performances
Pour quelques utilisateurs avec peu lignes par utilisateur, à peu près toutes la requête est rapide, même sans index.
Pour beaucoup utilisateurs et peu lignes par utilisateur, la première requête ci-dessus devrait être la plus rapide. Voir :
- Sélectionner la première ligne de chaque groupe GROUP BY ?
Pour beaucoup lignes par utilisateur, il y a (potentiellement beaucoup ) techniques plus rapides, en fonction des détails de votre configuration. Voir :
- Optimiser la requête GROUP BY pour récupérer la dernière ligne par utilisateur