1

私はテーブルを持っています:

create table table1 (event_id integer, event_time timestamp without time zone);
insert into table1 (event_id, event_time) values
(1, '2011-01-01 00:00:00'),
(2, '2011-01-01 00:00:15'),
(3, '2011-01-01 00:00:29'),
(4, '2011-01-01 00:00:58'),
(5, '2011-01-02 06:03:00'),
(6, '2011-01-02 06:03:09'),
(7, '2011-01-05 11:01:31'),
(8, '2011-01-05 11:02:15'),
(9, '2011-01-06 09:34:19'),
(10, '2011-01-06 09:34:41'),
(11, '2011-01-06 09:35:06');

イベントが与えられると、そのイベントで始まるイベントの「実行」の長さを返すことができるというステートメントを作成したいと思います。実行は次のように定義されます。

  1. 2つのイベントが互いに30秒以内にある場合、それらは一緒に実行されます。
  2. AとBが一緒に実行され、BとCが一緒に実行されている場合、AはCと一緒に実行されています。

ただし、クエリを過去にさかのぼる必要はないため、イベント2を選択した場合、イベント2、3、および4のみが、2で始まるイベントの実行の一部としてカウントされ、3は実行の長さ。

何か案は?私は困惑しています。

4

3 に答える 3

1

次のようになります。

WITH x AS (
    SELECT event_time
          ,row_number() OVER w AS rn
          ,lead(event_time) OVER w AS next_time
    FROM   table1
    WHERE  event_id >= <start_id>
    WINDOW w AS (ORDER BY event_time, event_id)
    )
SELECT COALESCE(
      (SELECT x.rn
       FROM   x
       WHERE  (x.event_time + interval '30s') < x.next_time
       ORDER  BY x.rn
       LIMIT  1)
     ,(SELECT count(*) FROM x)
      ) AS run_length

このバージョンは、ID のギャップのないシーケンスに依存するのではなく、event_timeのみに依存します。
同一の は、明確にするためにevent_time追加でソートされます。event_id

マニュアルのウィンドウ関数 row_number()CTE (With 句)について読んでlead()ください。

編集

event_id大きい方がより遅い (または等しい)と仮定できない場合は、最初の節event_timeを次のように置き換えます。WHERE

WHERE event_time >= (SELECT event_time FROM table1 WHERE event_id = <start_id>)

開始行と同じevent_timeで aa より小さい行event_idは無視されます。

最後まで 1 回実行するという特殊なケースでは、最後が見つからず、行が返されません。COALESCE代わりにすべての行の数を返します。

于 2011-11-23T18:09:24.137 に答える
1

これが RECURSIVE CTE ソリューションです。(島とギャップの問題は、自然に再帰的な CTE に役立ちます)

WITH RECURSIVE runrun AS (
    SELECT event_id, event_time
    , event_time - ('30 sec'::interval) AS low_time
    , event_time + ('30 sec'::interval) AS high_time
    FROM table1
    UNION
    SELECT t1.event_id, t1.event_time
    , LEAST ( rr.low_time, t1.event_time - ('30 sec'::interval) ) AS low_time
    , GREATEST ( rr.high_time, t1.event_time + ('30 sec'::interval) ) AS high_time
    FROM table1 t1
    JOIN runrun rr ON t1.event_time >= rr.low_time
                  AND t1.event_time < rr.high_time
    )
SELECT DISTINCT ON (event_id) *
FROM runrun rr
WHERE rr.event_time >= '2011-01-01 00:00:15'
AND rr.low_time <= '2011-01-01 00:00:15'
AND rr.high_time > '2011-01-01 00:00:15'
    ;

結果:

 event_id |     event_time      |      low_time       |      high_time      
----------+---------------------+---------------------+---------------------
        2 | 2011-01-01 00:00:15 | 2010-12-31 23:59:45 | 2011-01-01 00:00:45
        3 | 2011-01-01 00:00:29 | 2010-12-31 23:59:45 | 2011-01-01 00:01:28
        4 | 2011-01-01 00:00:58 | 2010-12-31 23:59:30 | 2011-01-01 00:01:28
(3 rows)
于 2011-11-23T22:42:32.030 に答える
1

日付差異ステートメントでテーブルをそれ自体に結合できます。実際、これはpostgresで、単純なマイナスが機能します。

このサブクエリは、「開始イベント」であるすべてのレコードを検索します。つまり、30 秒以内に別のイベント レコードが発生していないすべてのイベント レコード:

(Select a.event_id, a.event_time from
(Select event_id, event_time from table1) a
 left join 
 (select event_id, event_time from table1) b
 on a.event_time - b.event_time < '00:00:30' and a.event_time - b.event_time > '00:00:00'
 where b.event_time is null) startevent

いくつかの変更を加えて...「終了」イベントをピックアップすることを除いて、同じロジック:

(Select a.event_id, a.event_time from
(Select event_id, event_time from table1) a
 left join 
 (select event_id, event_time from table1) b
 on b.event_time - a.event_time < '00:00:30' and b.event_time - a.event_time > '00:00:00'
 where b.event_time is null) end_event

これで、これらを結合して、どの開始イベントがどの終了イベントにつながるかを関連付けることができます。

(まだ書いています...これにはいくつかの方法があります。例だけが線形ID番号を持っていると仮定しているので、開始イベント時間と終了イベント時間を結合して、正の差が最小になるようにする必要がありますイベント時間)。

これが私の最終結果です...多くのサブセレクトがネストされています

 select a.start_id, case when a.event_id is null then t1.event_id::varchar else 'single  event' end as end_id
 from
 (select start_event.event_id as start_id, start_event.event_time as start_time,      last_event.event_id, min(end_event.event_time - start_event.event_time) as min_interval   
 from
    (Select a.event_id, a.event_time from
    (Select event_id, event_time from table1) a
     left join 
    (select event_id, event_time from table1) b
   on a.event_time - b.event_time < '00:00:30' and a.event_time - b.event_time > '00:00:00'
 where b.event_time is null) start_event

inner join

   (Select a.event_id, a.event_time from
(Select event_id, event_time from table1) a
 left join 
 (select event_id, event_time from table1) b
 on b.event_time - a.event_time < '00:00:30' and b.event_time - a.event_time > '00:00:00'
 where b.event_time is null) end_event     
on end_event.event_time > start_event.event_time

--check for only event
 left join
 (Select a.event_id, a.event_time from
 (Select event_id, event_time from table1) a
  left join 
  (select event_id, event_time from table1) b
  on b.event_time - a.event_time < '00:00:30' and b.event_time - a.event_time > '00:00:00'
  where b.event_time is null) last_event
    on start_event.event_id = last_event.event_id
group by 1,2,3) a
    left join table1 t1 on t1.event_time = a.start_time + a.min_interval

start_id、end_Id としての結果:

1;"4"
5;"6"
7;"単一イベント"
8;"単一イベント"
9;"11"

開始イベントと終了イベントの両方であるイベントを検出する方法として、単一のイベントを選択するために 3 番目の左結合を使用する必要がありました。最終結果は ID にあり、ID だけでなく別の情報が必要な場合は、元のテーブルにリンクすることができます。何百万ものイベントがある場合、このソリューションがどのように拡張されるかわからない...問題になる可能性があります。

于 2011-11-23T19:09:49.433 に答える