タイム スタンプの範囲とユーザー ID を含む PostgreSQL (9.4) テーブルがあり、(同じユーザー ID を持つ) 重複する範囲を 1 つのレコードに折りたたむ必要があります。
これを達成するために複雑な CTE のセットを試してみましたが、(40,000 行以上) 実際のテーブルには、問題を複雑にするエッジケースがいくつかあります。おそらく再帰的な CTE が必要だという結論に達しましたが、それを書くのはうまくいきませんでした。
テスト テーブルを作成してデータを入力するコードを次に示します。これはテーブルの正確なレイアウトではありませんが、例としては十分に近いものです。
CREATE TABLE public.test
(
id serial,
sessionrange tstzrange,
fk_user_id integer
);
insert into test (sessionrange, fk_user_id)
values
('[2016-01-14 11:57:01-05,2016-01-14 12:06:59-05]', 1)
,('[2016-01-14 12:06:53-05,2016-01-14 12:17:28-05]', 1)
,('[2016-01-14 12:17:24-05,2016-01-14 12:21:56-05]', 1)
,('[2016-01-14 18:18:00-05,2016-01-14 18:42:09-05]', 2)
,('[2016-01-14 18:18:08-05,2016-01-14 18:18:15-05]', 1)
,('[2016-01-14 18:38:12-05,2016-01-14 18:48:20-05]', 1)
,('[2016-01-14 18:18:16-05,2016-01-14 18:18:26-05]', 1)
,('[2016-01-14 18:18:24-05,2016-01-14 18:18:31-05]', 1)
,('[2016-01-14 18:18:12-05,2016-01-14 18:18:20-05]', 3)
,('[2016-01-14 19:32:12-05,2016-01-14 23:18:20-05]', 3)
,('[2016-01-14 18:18:16-05,2016-01-14 18:18:26-05]', 4)
,('[2016-01-14 18:18:24-05,2016-01-14 18:18:31-05]', 2);
これを行うと、セッションが開始された時刻で並べ替えられることがわかりました。
select * from test order by fk_user_id, sessionrange
これを使用して、ウィンドウ関数を使用して、個々のレコードが前のレコードと重複しているかどうかを判断できます。
SELECT *, sessionrange && lag(sessionrange) OVER (PARTITION BY fk_user_id ORDER BY sessionrange)
FROM test
ORDER BY fk_user_id, sessionrange
ただし、これは、単一の前のレコードが現在のレコードと重複しているかどうかを検出するだけです (レコードの場所を参照してくださいid = 6
)。パーティションの先頭までさかのぼって検出する必要があります。
その後、重複するすべてのレコードをグループ化して、最も早いセッションの開始点と終了する最後のセッションの終了点を見つける必要があります。
私が見落としているこれを行う方法があると確信しています。これらの重複するレコードを折りたたむにはどうすればよいですか?