it-swarm.dev

Funkcja okna SQL z klauzulą ​​where?

Próbuję skorelować dwa typy zdarzeń dla użytkowników. Chcę zobaczyć wszystkie zdarzenia „B” wraz z najnowszym wydarzeniem „A” dla tego użytkownika przed zdarzeniem „A”. Jak to osiągnąć? W szczególności próbuję to zrobić w Postgres.

Miałem nadzieję, że możliwe jest użycie klauzuli „where” w funkcji okna, w którym to przypadku mógłbym w zasadzie wykonać LAG () z „where event =” A ”, ale nie wydaje się to możliwe.

Jakieś zalecenia?

Przykład danych:

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

Pożądany rezultat:

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

Właśnie wypróbowałem podejście Gordona używając PostgreSQL 9.5.4 i to narzekało

FILTER nie jest zaimplementowany dla funkcji okien niezagregowanych

co oznacza, że ​​używanie lag() z FILTER nie jest dozwolone. Zmodyfikowałem więc zapytanie Gordona używając funkcji max(), innej ramki okna i 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';

Zweryfikowano, że działa to z PostgreSQL 9.5.4.

Podziękowania dla Gordona za sztuczkę FILTER!

13
Cheng Lian

Oto jedna metoda:

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

Możliwe, że skorelowane podzapytanie/połączenie boczne będzie miało lepszą wydajność.

4
Gordon Linoff

Nie ma potrzeby korzystania z funkcji okna. Znajdź wszystkie zdarzenia B i dla każdego z nich znajdź najnowszą zmienną A tego samego użytkownika za pomocą podzapytania. Coś takiego powinno to zrobić:

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';

Zakładam, że tabela nazywa się t (użyłem jej dwukrotnie).

1
redneb