あなたの定義:
グループBのアクティビティは、常にグループAのアクティビティの後に行われます。
..論理的には、ユーザーごとに、1つ以上のAアクティビティの後に0または1つのBアクティビティがあることを意味します。連続して1Bを超えるアクティビティはありません。
単一のウィンドウ関数DISTINCT ON
で動作させることができます およびCASE
、これは少数の最速の方法です。 ユーザーあたりの行数(以下も参照):
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<>ここでフィドル
SQL CASE
式のデフォルトはNULL
ELSE
がない場合 ブランチが追加されたので、短くしました。
time
を想定 定義されているNOT NULL
。それ以外の場合は、NULLS LAST
を追加することをお勧めします 。なぜですか?
- 列ASCで並べ替えますが、最初にNULL値を使用しますか?
(activity LIKE 'A%' OR activity LIKE 'B%')
activity ~ '^[AB]'
よりも冗長です 、ただし、Postgresの古いバージョンでは通常高速です。パターンマッチングについて:
- PostgreSQLのLIKE、SIMILAR TO、または正規表現とのパターンマッチング
条件付きウィンドウ関数?
それは実際には可能です 。集約されたFILTER
を組み合わせることができます OVER
を含む句 ウィンドウ関数の句。 ただし :
-
FILTER
句自体は、現在の行の値でのみ機能します。 -
さらに重要なのは、
FILTER
lead()
のような純粋な本物の関数には実装されていません またはlag()
(Postgres 13まで)-集計関数のみ。
試してみる場合:
lead(activity) FILTER (WHERE activity LIKE 'A%') OVER () AS activity
Postgresはあなたに教えてくれます:
FILTER is not implemented for non-aggregate window functions
FILTER
について :
- 追加の(個別の)フィルターを使用して列を集約します
- ウィンドウ関数のFILTER句で現在の行を参照する
パフォーマンス
少数の場合 少数のユーザー ユーザーあたりの行数、ほぼ インデックスがなくてもクエリは高速です。
多くの場合 ユーザーと少数 ユーザーあたりの行数、上記の最初のクエリが最速である必要があります。参照:
- 各GROUPBYグループの最初の行を選択しますか?
多くの場合 ユーザーあたりの行数は、(潜在的に多く )セットアップの詳細に応じて、より高速なテクニック。参照:
- GROUP BYクエリを最適化して、ユーザーごとに最新の行を取得します