1

以下のクエリでは、PostgreSQL を使用して 2 時間間隔の最小値、最大値、および平均値を計算しています。

クエリは偶数開始時間でも問題なく動作します(..04:00:00+05:30)が、奇数開始時間の偶数開始時間と同様の結果が得られます(..05:00:00+05:30)。2 倍すると偶数時間が返されますが、これが問題です。

SELECT tagid, CAST(sample_time_stamp as Date) AS stat_date, 
       (floor(extract(hour from sample_time_stamp)/2) * 2)::int AS hrs,
       min(sensor_reading) AS theMin,   
       max(sensor_reading) AS theMax,
       avg(sensor_reading) AS theAvg
FROM sensor_readings WHERE tagid =1 AND 
sample_time_stamp BETWEEN '2012-10-23 01:00:00+05:30'
                  AND     '2012-10-23 05:59:00+05:30'
GROUP BY tagid,CAST(sample_time_stamp as Date),
         floor(extract(hour from sample_time_stamp)/2) * 2
ORDER BY tagid,stat_date, hrs

奇数開始時間の出力 ('2012-10-23 01:00:00+05:30')

tagid    date          hrs  theMin  themax    theAvg 
1        2012-10-23    0    6       58        30.95
1        2012-10-23    2    2       59        29.6916666666667
1        2012-10-23    4    3       89        31.7666666666667

偶数開始時間の出力 ('2012-10-23 02:00:00+05:30')

tagid    date          hrs  theMin   themax    theAvg
1        2012-10-23    2    2        59        29.6916666666667
1        2012-10-23    4    3        89        31.7666666666667
4

1 に答える 1

3

最小のタイムスタンプから始まる一定の時間枠を取得するには:

WITH params AS (
   SELECT '2012-10-23 01:00:00+05:30'::timestamptz AS _min  -- input params
         ,'2012-10-23 05:59:00+05:30'::timestamptz AS _max
         ,'2 hours'::interval                      AS _interval
   )
  ,ts AS (SELECT generate_series(_min, _max, _interval) AS t_min FROM params)
  ,timeframe AS (
   SELECT t_min
         ,lead(t_min, 1, _max) OVER (ORDER BY t_min) AS t_max
   FROM ts, params
   )
SELECT s.tagid
      ,t.t_min
      ,t.t_max     -- mildly redundant except for last row
      ,min(s.sensor_reading) AS the_min
      ,max(s.sensor_reading) AS the_max
      ,avg(s.sensor_reading) AS the_avg
FROM   timeframe t
LEFT   JOIN sensor_readings s ON  s.tagid = 1
                              AND s.sample_time_stamp >= t.t_min
                              AND s.sample_time_stamp <  t.t_max
GROUP  BY 1,2,3
ORDER  BY 1,2;

任意の時間枠と間隔の長さに使用できますPostgreSQL 8.4 以降が必要です。

最大タイムスタンプが最後の時間枠に収まらない場合、切り捨てられます_max_min + n * _intervalしたがって、最後の行は、希望する時間枠よりも短い時間枠を表す可能性があります_interval

重要な要素

  • 処理を容易にする共通テーブル式 (CTE) 。上部の CTE にパラメーター値を 1 回params入力します。

  • generate_series()時間ラスターを作成するための間隔。

  • lead(...)3 つのパラメーター (デフォルトを含む) を持つウィンドウ関数- 最後の行の特殊なケースをカバーします。

  • LEFT JOINラスターと実際のデータの間で、一致するデータのない時間枠が引き続き結果に表示されるようにします (NULL値をデータとして)。それが後の編集の理由でもあります。それを達成するWHEREには、条件を条件に移動する必要がありました。LEFT JOIN

再帰的 CTE を使用した代替時間枠の生成:

WITH RECURSIVE params AS (
   SELECT '2012-10-23 01:00:00+05:30'::timestamptz AS _min  -- input params
         ,'2012-10-23 05:59:00+05:30'::timestamptz AS _max
         ,'2 hours'::interval                      AS _interval
   )
   , timeframe AS (
   SELECT _min AS t_min, LEAST(_min + _interval, _max) AS t_max
   FROM   params

   UNION  ALL
   SELECT t_max, LEAST(t_max + _interval, _max)
   FROM   timeframe t, params p
   WHERE  t_max < _max
   )
SELECT ...

わずかに速い...あなたの選択をしてください。
->両方を表示するsqlfiddle

宣言されている場合でも、(さらに)非再帰的なCTEを持つことができることに注意してくださいWITH RECURSIVE

パフォーマンスとインデックス

元のクエリよりも高速である必要があります。コードの半分は時間ラスターの生成を処理します。これは数行に関係し、非常に高速です。実際のテーブル行 (高価な部分) の処理は安くなります。これは、新しい値を毎回計算しなくなるためsample_time_stampです。

次の形式の複数列インデックスが必要です。

CREATE INDEX foo_idx ON sensor_readings (tagid, sample_time_stamp DESC);

DESC最近のエントリ (より後のタイムスタンプ) をより頻繁にクエリするという前提で使用します。そうでない場合は、修飾子を削除します。どちらでも大差ありません。

于 2012-11-02T21:05:31.883 に答える