web-dev-qa-db-ja.com

PostgreSQL-first_value()を使用するにはどうすればよいですか?

この回答は、ティッカーから高値/安値/始値/終値を生成する方法を示しています。
任意の時間間隔で集計を取得

これに基づいてソリューションを実装しようとしていますが(PG 9.2)、first_value()の正しい値を取得するのが困難です。

これまでに、2つのクエリを試しました。

_SELECT  
    cstamp,
    price,
    date_trunc('hour',cstamp) AS h,
    floor(EXTRACT(minute FROM cstamp) / 5) AS m5,
    min(price) OVER w,
    max(price) OVER w,
    first_value(price) OVER w,
    last_value(price) OVER w
FROM trades
Where date_trunc('hour',cstamp) = timestamp '2013-03-29 09:00:00'
WINDOW w AS (
    PARTITION BY date_trunc('hour',cstamp), floor(extract(minute FROM cstamp) / 5)
    ORDER BY date_trunc('hour',cstamp) ASC, floor(extract(minute FROM cstamp) / 5) ASC
    )
ORDER BY cstamp;
_

これが結果の一部です。

_        cstamp         price      h                 m5  min      max      first    last
"2013-03-29 09:19:14";77.00000;"2013-03-29 09:00:00";3;77.00000;77.00000;77.00000;77.00000

"2013-03-29 09:26:18";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000
"2013-03-29 09:29:41";77.80000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000
"2013-03-29 09:29:51";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.80000;77.00000

"2013-03-29 09:30:04";77.00000;"2013-03-29 09:00:00";6;73.99004;77.80000;73.99004;73.99004
_

ご覧のとおり、77.8はではありませんfirst_value()の正しい値であると私が信じている値は77.0です。

これはWINDOWの_ORDER BY_があいまいなためかもしれませんが、これを

_ORDER BY cstamp ASC 
_

しかし、これはPARTITIONも混乱させるようです:

_        cstamp         price      h                 m5  min      max      first    last
"2013-03-29 09:19:14";77.00000;"2013-03-29 09:00:00";3;77.00000;77.00000;77.00000;77.00000

"2013-03-29 09:26:18";77.00000;"2013-03-29 09:00:00";5;77.00000;77.00000;77.00000;77.00000
"2013-03-29 09:29:41";77.80000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.00000;77.80000
"2013-03-29 09:29:51";77.00000;"2013-03-29 09:00:00";5;77.00000;77.80000;77.00000;77.00000

"2013-03-29 09:30:04";77.00000;"2013-03-29 09:00:00";6;77.00000;77.00000;77.00000;77.00000
_

maxとlast nowの値はパーティション内で変化するため

何が悪いのですか? PARTITION内のORDERWINDOWの関係を理解するのを手伝ってくれませんか?


答えはありますが、誰でもテーブルを再作成できるように、トリミングされたpg_dumpを以下に示します。唯一異なるのは、テーブル名です。

_CREATE TABLE wtest (
    cstamp timestamp without time zone,
    price numeric(10,5)
);

COPY wtest (cstamp, price) FROM stdin;
2013-03-29 09:04:54 77.80000
2013-03-29 09:04:50 76.98000
2013-03-29 09:29:51 77.00000
2013-03-29 09:29:41 77.80000
2013-03-29 09:26:18 77.00000
2013-03-29 09:19:14 77.00000
2013-03-29 09:19:10 77.00000
2013-03-29 09:33:50 76.00000
2013-03-29 09:33:46 76.10000
2013-03-29 09:33:15 77.79000
2013-03-29 09:30:08 77.80000
2013-03-29 09:30:04 77.00000
\.
_
19

SQLフィドル

使用したすべての関数は、パーティションではなくウィンドウフレームで機能します。省略した場合、フレームの終わりが現在の行になります。ウィンドウフレームをパーティション全体にするには、フレーム句(range...)で宣言します。

SELECT  
    cstamp,
    price,
    date_trunc('hour',cstamp) AS h,
    floor(EXTRACT(minute FROM cstamp) / 5) AS m5,
    min(price) OVER w,
    max(price) OVER w,
    first_value(price) OVER w,
    last_value(price) OVER w
FROM trades
Where date_trunc('hour',cstamp) = timestamp '2013-03-29 09:00:00'
WINDOW w AS (
    PARTITION BY date_trunc('hour',cstamp) , floor(extract(minute FROM cstamp) / 5)
    ORDER BY cstamp
    range between unbounded preceding and unbounded following
    )
ORDER BY cstamp;
24
Clodoaldo Neto

次に、動作を説明する簡単なクエリを示します。

_select 
  v,
  first_value(v) over w1 f1,
  first_value(v) over w2 f2,
  first_value(v) over w3 f3,
  last_value (v) over w1 l1,
  last_value (v) over w2 l2,
  last_value (v) over w3 l3,
  max        (v) over w1 m1,
  max        (v) over w2 m2,
  max        (v) over w3 m3,
  max        (v) over () m4
from (values(1),(2),(3),(4)) t(v)
window
  w1 as (order by v),
  w2 as (order by v rows between unbounded preceding and current row),
  w3 as (order by v rows between unbounded preceding and unbounded following)
_

上記のクエリの出力は次のとおりです( SQLFiddle here ):

_| V | F1 | F2 | F3 | L1 | L2 | L3 | M1 | M2 | M3 | M4 |
|---|----|----|----|----|----|----|----|----|----|----|
| 1 |  1 |  1 |  1 |  1 |  1 |  4 |  1 |  1 |  4 |  4 |
| 2 |  1 |  1 |  1 |  2 |  2 |  4 |  2 |  2 |  4 |  4 |
| 3 |  1 |  1 |  1 |  3 |  3 |  4 |  3 |  3 |  4 |  4 |
| 4 |  1 |  1 |  1 |  4 |  4 |  4 |  4 |  4 |  4 |  4 |
_

_ORDER BY_句を使用するウィンドウ関数に適用される暗黙的なフレームについて考える人はほとんどいません。この場合、ウィンドウはデフォルトでフレーム_ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW_になります。次のように考えてください。

  • _v = 1_のある行では、順序付けられたウィンドウのフレームはv IN (1)にまたがります
  • _v = 2_のある行では、順序付けられたウィンドウのフレームはv IN (1, 2)にまたがります
  • _v = 3_のある行では、順序付けられたウィンドウのフレームはv IN (1, 2, 3)にまたがります
  • _v = 4_のある行では、順序付けられたウィンドウのフレームはv IN (1, 2, 3, 4)にまたがります

その動作を防止したい場合は、2つのオプションがあります。

  • orderedウィンドウ関数に明示的な_ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING_句を使用する
  • 省略可能なウィンドウ関数では_ORDER BY_句を使用しない(MAX(v) OVER()として)

詳細は LEAD()LAG()FIRST_VALUE()およびLAST_VALUE() に関するこの記事で説明されています

13
Lukas Eder

ウィンドウ関数としてのmax()の結果は、フレーム定義に基づいています。

デフォルトのフレーム定義(_ORDER BY_を使用)は、フレームの先頭から現在の行の最後のピア(現在の行および場合によってはより多くの行のランキングを含む)等しいに従って_ORDER BY_)。 _ORDER BY_がない場合(私の回答のようにあなたが参照している場合)、または_ORDER BY_がパーティションのすべての行を等しいものとして扱う場合(最初の例のように)、パーティションのすべての行はピアです、およびmax()は、パーティションのすべての行に対して同じ結果を生成し、パーティションのall行を効果的に考慮します。

ドキュメントごと:

デフォルトのフレーミングオプションは_RANGE UNBOUNDED PRECEDING_で、これは_RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW_と同じです。 _ORDER BY_を使用すると、フレームはパーティションの先頭からすべての行になるように設定されます現在の行の最後のピアまで。 _ORDER BY_がない場合、すべての行が現在の行のピアになるため、パーティションのすべての行がウィンドウフレームに含まれます。

大胆な強調鉱山。

単純な解決策は、ウィンドウ定義で_ORDER BY_を省略することです-参照している例で示したように。

マニュアルの Window Function Calls の章にあるフレーム仕様に関するすべての気の毒な詳細。

4