PostgreSQL-как использовать первое значение()?

этот ответ показывает, как создавать высокие/низкие/Открытые / Закрытые значения из тикера:
получить агрегаты для произвольных интервалов времени

Я пытаюсь реализовать решение на основе этого (стр. 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 BY на WINDOW, поэтому я изменил это на

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 и ORDER внутри WINDOW?


хотя у меня есть ответ, вот обрезанный 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
.

3 ответов


SQL Fiddle

все функции, которые вы использовали, действуют на оконную раму, а не на раздел. Если опущено, конец кадра-это текущая строка. Чтобы сделать рамку окна всей секцией, объявите ее в предложении 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;

вот быстрый запрос, чтобы проиллюстрировать поведение:

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 предложения. В этом случае windows по умолчанию для 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 статьи приказал окне функции
  • использовать нет ORDER BY предложение в тех оконных функциях, которые позволяют опустить их (как MAX(v) OVER())

более подробная информация объясняется в этой статье LEAD(), LAG(), FIRST_VALUE() и LAST_VALUE()


результат max() поскольку функция окна основана на определение фрейма.

определение фрейма по умолчанию (с ORDER BY) от начала кадра до последний узел текущей строки (включая текущую строку и, возможно, больше строк рейтинга одинаково по данным ORDER BY). В отсутствие ORDER BY (как в моем ответе, на который вы ссылаетесь), или если ORDER BY рассматривает каждую строку в разделе как равную (например в первом примере) все строки в разделе являются одноранговыми и max() дает тот же результат для каждой строки в разделе, эффективно учитывая все строк раздела.

в документации:

вариант кадрирования по умолчанию:RANGE UNBOUNDED PRECEDING, который является то же самое, что RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. С ORDER BY, это устанавливает фрейм как все строки от начала раздела вверх по текущей строке последний Пэр. Без ORDER BY, все строки разделы включены в оконную рамку, так как все строки становятся одноранговые узлы текущей строки.

жирным выделено мной.

простым решением было бы пропустить ORDER BY в определении окна-так же, как я продемонстрировал в Примере, на который вы ссылаетесь.

все кровавые подробности о спецификациях кадров в главе Функция Окна Звонки в руководстве.