web-dev-qa-db-ja.com

where句を含むSQLウィンドウ関数?

ユーザーのために2種類のイベントを関連付けようとしています。 「A」イベントの前のそのユーザーの最新のイベント「A」とともに、すべてのイベント「B」を表示したい。どうすればこれを達成できますか?特に、Postgresでこれを実行しようとしています。

ウィンドウ関数で「where」句を使用できることを望んでいました。この場合、「where event = 'A'」でLAG()を実行できますが、それは不可能のようです。

何かお勧めですか?

データ例:

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

望ましい結果:

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

PostgreSQL 9.5.4を使用してGordonのアプローチを試したところ、

FILTERは非集約ウィンドウ関数には実装されていません

つまり、FILTERlag()を使用することはできません。そこで、別のウィンドウフレームであるmax()とCTEを使用してGordonのクエリを変更しました。

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

これがPostgreSQL 9.5.4で動作することを確認しました。

FILTERトリックを提供してくれたGordonに感謝します。

13
Cheng Lian

これが1つの方法です。

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

相関サブクエリ/ラテラル結合の方がパフォーマンスが優れている可能性があります。

4
Gordon Linoff

ここではウィンドウ関数は必要ありません。すべてのBイベントを検索し、それらのそれぞれについて、サブクエリを介して同じユーザーの最新のAを検索します。そのような何かがそれをするはずです:

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

テーブルはtと呼ばれていると思います(2回使用しました)。

1
redneb