私はこのようなテーブルを持っています:
_Name activity time
user1 A1 12:00
user1 E3 12:01
user1 A2 12:02
user2 A1 10:05
user2 A2 10:06
user2 A3 10:07
user2 M6 10:07
user2 B1 10:08
user3 A1 14:15
user3 B2 14:20
user3 D1 14:25
user3 D2 14:30
_
今、私はこのような結果が必要です:
_Name activity next_activity
user1 A2 NULL
user2 A3 B1
user3 A1 B2
_
すべてのユーザーについて、グループAの最後のアクティビティと、グループBの次に発生したアクティビティの種類を確認したいと思います(グループBのアクティビティは、常にグループAのアクティビティの後に発生します)。他の種類の活動は私にとって面白くありません。 lead()
関数を使おうとしましたが、うまくいきませんでした。
どうすれば問題を解決できますか?
テスト設定:
_CREATE TEMP TABLE t (name text, activity text, time time);
INSERT INTO t values
('user1', 'A1', '12:00')
,('user1', 'E3', '12:01')
,('user1', 'A2', '12:02')
,('user2', 'A1', '10:05')
,('user2', 'A2', '10:06')
,('user2', 'A3', '10:07')
,('user2', 'M6', '10:07')
,('user2', 'B1', '10:08')
,('user3', 'A1', '14:15')
,('user3', 'B2', '14:20')
,('user3', 'D1', '14:25')
,('user3', 'D2', '14:30');
_
あなたの定義:
グループ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;
_
CASE
ブランチが追加されていない場合、SQL NULL
式はデフォルトでELSE
になるので、短くしました。
また、time
が定義されていると仮定します_NOT NULL
_。それ以外の場合は、_NULLS LAST
_を追加することをお勧めします。どうして?
_(activity LIKE 'A%' OR activity LIKE 'B%')
_は_activity ~ '^[AB]'
_よりも冗長ですが、通常、古いバージョンのPostgresでは高速です。パターンマッチングについて:
それは実際には可能です。集約FILTER
句をウィンドウ関数のOVER
句と組み合わせることができます。 ただし:
FILTER
句自体は、現在の行の値でのみ機能します。
さらに重要なことに、Postgres9.6のlead()
やlag()
のような純粋なウィンドウ関数にはFILTER
は実装されていません(まだ) 集計関数 のみ。
試してみると:
_lead(activity) FILTER (WHERE activity LIKE 'A%') OVER () AS activity
_
Postgresはあなたに言うでしょう:
_FILTER is not implemented for non-aggregate window functions
_
FILTER
について:
(少数ユーザーの場合ユーザーあたりの行数が少ない、ほとんどanyクエリは、インデックスがなくても高速です。)
多くのユーザーおよびの場合ユーザーあたりの行数が少ないの場合、上記の最初のクエリが最も高速になります。インデックスとパフォーマンスについては、上記の リンクされた回答 を参照してください。
ユーザーあたり多くの行の場合、(潜在的に多くの)セットアップの他の詳細に応じて、より高速なテクニック:
select distinct on(name) name,activity,next_activity
from (select name,activity,time
,lead(activity) over (partition by name order by time) as next_activity
from t
where left(activity,1) in ('A','B')
) t
where left(activity,1) = 'A'
order by name,time desc