web-dev-qa-db-fra.com

Fonction de fenêtre SQL avec une clause where?

J'essaie de corréler deux types d'événements pour les utilisateurs. Je souhaite voir tous les événements "B" ainsi que l'événement "A" le plus récent pour cet utilisateur avant l'événement "A". Comment pourrait-on accomplir cela? En particulier, j'essaie de faire cela dans Postgres.

J'espérais qu'il était possible d'utiliser une clause "where" dans une fonction window, auquel cas je pouvais essentiellement faire un LAG () avec un "where événement = 'A'", mais cela ne semble pas possible.

Des recommandations?

Exemple de données:

|user |time|event|
|-----|----|-----|
|Alice|1   |A    |
|Bob  |2   |A    |
|Alice|3   |A    |
|Alice|4   |B    |
|Bob  |5   |B    |
|Alice|6   |B    |

Résultat désiré:

|user |event_b_time|last_event_a_time|
|-----|------------|-----------------|
|Alice|4           |3                |
|Bob  |5           |2                |
|Alice|6           |3                |
8
MJ.

Je viens d'essayer l'approche de Gordon en utilisant PostgreSQL 9.5.4, et il s'est plaint que

FILTER n'est pas implémenté pour les fonctions de fenêtre non agrégées

ce qui signifie que l'utilisation de lag() avec FILTER n'est pas autorisée. J'ai donc modifié la requête de Gordon en utilisant max(), un cadre de fenêtre différent et CTE:

WITH subq AS (
  SELECT
    "user", event, time as event_b_time,
    max(time) FILTER (WHERE event = 'A') OVER (
      PARTITION BY "user"
      ORDER BY time
      ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
    ) AS last_event_a_time
  FROM events
  ORDER BY time
)
SELECT
  "user", event_b_time, last_event_a_time
FROM subq
WHERE event = 'B';

Vérifié que cela fonctionne avec PostgreSQL 9.5.4.

Merci à Gordon pour le tour FILTER!

13
Cheng Lian

Voici une méthode:

select t.*
from (select t.*,
             lag(time) filter (where event = 'A') (partition by user order by time)
      from t
     ) t
where event = 'B';

Il est possible que la jointure latérale/sous-requête corrélée ait de meilleures performances.

4
Gordon Linoff

Il n'y a pas besoin de fonctions de fenêtre ici. Il suffit de rechercher tous les événements B et, pour chacun d'entre eux, de rechercher la plus récente A du même utilisateur via une sous-requête. Quelque chose comme ça devrait le faire:

SELECT
    "user",
    time AS event_b_time,
    (SELECT time AS last_event_a_time
     FROM t t1
     WHERE "user"=t.user AND event='A' AND time<t.time
     ORDER BY time DESC LIMIT 1)
FROM t
WHERE event='B';

Je suppose que la table s'appelle t (je l'ai utilisée deux fois).

1
redneb