タイムスタンプを10分間隔で最も近い10分間にグループ化し、それぞれの最小および最大のタイムスタンプを含むビューを作成する方法を知りたいです。
したがって、次のようなテーブルです。
| Hero | timestamp |
| Batman | 2016-12-08 12:00:00 |
| Batman | 2016-12-08 12:07:00 |
| Batman | 2016-12-08 13:00:00 |
| Batman | 2016-12-08 14:00:00 |
| Wonder Woman | 2016-12-08 10:15:00 |
| Wonder Woman | 2016-12-08 10:18:00 |
| Wonder Woman | 2016-12-08 10:25:00 |
| Wonder Woman | 2016-12-08 10:30:00 |
このようなビューになります
| Hero | start_time | end_time |
| Batman | 2016-12-08 12:00:00 | 2016-12-08 12:07:00 |
| Wonder Woman | 2016-12-08 10:15:00 | 2016-12-08 10:30:00 |
またはこれ:
| Hero | start_time | end_time |
| Batman | 2016-12-08 13:00:00 | NULL |
| Batman | 2016-12-08 14:00:00 | NULL |
| Batman | 2016-12-08 12:00:00 | 2016-12-08 12:07:00 |
| Wonder Woman | 2016-12-08 10:15:00 | 2016-12-08 10:30:00|
どちらのソリューションでも問題ありません。
「時間」と10分間隔の組み合わせでグループ化することをお勧めします。
_SELECT hero
, min(timestamp) AS start_time
, CASE WHEN count(*) > 1 THEN max(timestamp) END AS end_time
FROM tbl
GROUP BY hero
, date_trunc('hour', timestamp)
, EXTRACT(MINUTE FROM timestamp)::int / 10
ORDER BY 1, 2; -- optional
_
EXTRACT(minute FROM timestamp)
は、時間の分の部分を抽出する式です。整数(_::int
_)へのキャスト後、整数除算(_/ 10
_)は、実質的に10分間隔に丸められます(_0
_-_5
_)。
CASE
式は、同じ10分の間隔に複数の行が含まれる場合にのみ_end_time
_を追加します。
「タイムスタンプ」を識別子として使用しないことをお勧めします。これは、標準SQLでは予約語であり、Postgresでは基本データ型です。
「グループ」が同じヒーローの列間の10分以上のギャップによって定義されている場合:
_SELECT hero
, count(*) AS ct -- optional
, min(timestamp) AS start_time
, CASE WHEN count(*) > 1 THEN max(timestamp) END AS end_time
FROM (
SELECT hero, timestamp, count(step OR NULL) OVER (ORDER BY hero, timestamp) AS grp
FROM (
SELECT *
, lag(timestamp) OVER (PARTITION BY hero ORDER BY timestamp)
< timestamp - interval '10 min' AS step
FROM tbl
) sub1
) sub2
GROUP BY hero, grp;
_
詳細な説明:
SQL Fiddle 両方の場合。
時間間隔を丸める代わりに整数を丸めたい場合は、「最も近い1ダース」にすると、次の操作を実行します。
number::integer / 12 * 12
12で除算したり乗算したりすると、「何もしない」ように見えますが、実際には、数値の「ダースの1ダース」は取り除かれます。秘訣は、除算が整数除算であり、結果が小数部分のない整数であるという事実にあります。
たとえば、試してみてください:
SELECT
1 / 12 * 12 AS n1,
2 / 12 * 12 AS n2,
11 / 12 * 12 AS n11,
12 / 12 * 12 AS n12,
13 / 12 * 12 AS n13,
23 / 12 * 12 AS n23,
24 / 12 * 12 AS n24,
25 / 12 * 12 AS n25,
35 / 12 * 12 AS n35 ;
これは、特定のビンサイズ(この場合は12)を指定して、数値を「切り捨て」てビンに入れるための標準的な方法です。
タイムスタンプを整数(またはbigints)に変換するのと同じように、時間についても同じことができます。そのためには、タイムスタンプを「特定の日時からの秒数」として表現します。タイムスタンプの " Epoch "は、特定の日時(1970年1月1日00:00:00)から経過した秒数を表します。 PostgreSQL(そして一般的にはSQLだと思います)は、この値を取得するために「EXTRACT(Epoch FROM ts)」を使用します。
これらのエポックを任意の間隔に丸めたい場合は(この場合は10分= 60 * 10秒)、このエポックを整数で600で除算し(小数を取り除く)、この600で乗算します。このようにして、10分の間隔の「端数部分」を取り除きます。
次のコードは、考えられるソリューションの2番目のバージョンを提供します(ワンダーウーマン用にいくつかの追加の行があります)。
CREATE TABLE hero_stamps (hero text, ts timestamp without time zone) ;
INSERT INTO
hero_stamps (hero, ts)
VALUES
('Batman', '2016-12-08 12:00:00'),
('Batman', '2016-12-08 12:07:00'),
('Batman', '2016-12-08 13:00:00'),
('Batman', '2016-12-08 14:00:00'),
('Wonder Woman', '2016-12-08 10:15:00'),
('Wonder Woman', '2016-12-08 10:18:00'),
('Wonder Woman', '2016-12-08 10:25:00'),
('Wonder Woman', '2016-12-08 11:30:00') ;
SELECT
hero,
start_time,
(CASE WHEN start_time = end_time THEN NULL ELSE end_time END) AS end_time
FROM
(
SELECT
hero,
extract(Epoch from ts)::bigint /* timestamp converted to Epoch */
/ (60 * 10)::integer /* integer div */
* (60 * 10)::integer /* multiply back to have start time */
AS Epoch_start_time_of_interval,
min(ts) AS start_time,
max(ts) AS end_time
FROM
hero_stamps
GROUP BY
hero, Epoch_start_time_of_interval
) AS s0
ORDER BY
hero, start_time ;
GROUP BYは「ビニング」を行い、min(ts)とmax(ts)は最大値と最小値を提供し、最も外側のSELECTの追加関数はnullを希望どおりに提供します(そして「Epoch_start_time_of_interval」を取り除きます「あなたが興味を持たないこと)。
この方法は、「時間」と「分」が同じでカレンダーの日付が異なるタイムスタンプがある場合でも機能します。これは、この情報が無視されないためです(「エポック」に埋め込まれます)。
よりコンパクトなバージョン(同じ結果が得られる)は
SELECT
hero,
min(ts) AS start_time,
(CASE WHEN count(*) = 1 THEN NULL ELSE max(ts) END) AS end_time
FROM
hero_stamps
GROUP BY
hero,
extract(Epoch from ts)::bigint / 600
ORDER BY
1, 2 ;