21

这个答案显示了如何从股票代码中产生高/低/打开/关闭值:
检索任意时间间隔的聚合

我正在尝试基于此(PG 9.2)实施解决方案,但难以获得正确的first_value().

到目前为止,我已经尝试了两个查询:

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。

我虽然这可能是由于 中的模棱两可ORDER BYWINDOW所以我将其更改为

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 的值现在在分区内有所不同

我究竟做错了什么?有人可以帮助我更好地理解 a 之间的关系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
\.
4

3 回答 3

27

SQL小提琴

您使用的所有功能都作用于窗框,而不是隔板。如果省略,则帧结束是当前行。要使窗框成为整个分区,请在 frame 子句 ( 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;
于 2013-03-30T21:52:50.920 回答
19

这是一个快速查询来说明行为:

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):

| 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子句的窗口函数的隐式框架。在这种情况下,窗口默认为 frame 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)

如果你想阻止这种行为,你有两个选择:

  • 对有序窗口函数使用显式ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING子句
  • Use no ORDER BY clause in those window functions that allow for omitting them (as MAX(v) OVER())

More details are explained in this article about LEAD(), LAG(), FIRST_VALUE() and LAST_VALUE()

于 2014-11-07T17:19:56.170 回答
4

as 窗口函数的结果max()是基于框架定义的。

默认的帧定义(带有ORDER BY)是从帧的开始到当前行的最后一个对等点(包括当前行和可能更多的行,根据相同排名ORDER BY)。在没有ORDER BY(就像您所指的我的回答中),或者如果ORDER BY将分区中的每一行视为相等(如在您的第一个示例中),则分区中的所有行都是对等的,并且max()对中的每一行产生相同的结果分区,有效地考虑分区的所有行。

根据文档:

默认框架选项是RANGE UNBOUNDED PRECEDING,与 相同RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW使用ORDER BY,这会将框架设置为从分区开始 到当前行的最后一个 peer 的所有行。如果没有ORDER BY,则分区的所有行都包含在窗口框架中,因为所有行都成为当前行的对等点。

大胆强调我的。

简单的解决方案是在窗口定义中省略ORDER BY- 就像我在您所指的示例中演示的那样。

手册中的窗口函数调用一章中有关框架规范的所有详细信息。

于 2013-03-30T21:51:26.390 回答