Используйте функции окна T-SQL для извлечения 5-минутных средних из 1-минутных данных - PullRequest
4 голосов
/ 04 ноября 2019

У меня есть таблица базы данных, содержащая одноминутные периоды значений Open, Close, High, Low, Volume для ценной бумаги. Я использую SQL Server 2017, но вариант 2019 RC - это вариант.

Я пытаюсь найти эффективный запрос SQL Server, который может объединить их в 5-минутные окна, где:

  • Open = первый Open значение окна
  • Close = last Закрыть значение окна
  • High = max Высокое значение окна
  • Low = min Low значениеокно
  • Volume = avg Объем по окну

В идеале этот запрос должен учитывать пробелы в данных, т. е. основываться на вычислениях даты, а не на предыдущих или последующих строках.

Например, скажите, что у меня есть (вот 6 минут данных):

| Time             | Open | Close | High | Low | Volume |
|------------------|------|-------|------|-----|--------|
| 2019-10-30 09:30 | 5    | 10    | 15   | 1   | 125000 |
| 2019-10-30 09:31 | 10   | 15    | 20   | 5   | 100000 |
| 2019-10-30 09:32 | 15   | 20    | 25   | 10  | 120000 |
| 2019-10-30 09:33 | 20   | 25    | 30   | 15  | 10000  |
| 2019-10-30 09:34 | 20   | 22    | 40   | 2   | 13122  |
| 2019-10-30 09:35 | 22   | 30    | 35   | 4   | 15000  | Not factored in, since this would be the first row of the next 5-minute window

Я пытаюсь написать запрос, который даст мне (вот первый пример 5-минутного агрегата):

| Time             | Open | Close | High | Low | Volume  |
|------------------|------|-------|------|-----|---------|
| 2019-10-30 09:30 | 5    | 30    | 40   | 1   | 50224.4 |

Есть советы? Я бьюсь головой об стену с предложением OVER и его параметрами PARTITION / RANGE

Ответы [ 3 ]

5 голосов
/ 04 ноября 2019

Суть проблемы заключается в округлении значений даты и времени до 5-минутной границы, что (при условии, что тип данных datetime) может быть выполнено с использованием DATEADD(MINUTE, DATEDIFF(MINUTE, 0, time) / 5 * 5, 0). Отдых - это основные функции группирования / окна:

WITH cte AS (
  SELECT clamped_time
       , [Open]
       , [Close]
       , [High]
       , [Low]
       , [Volume]
       , rn1 = ROW_NUMBER() OVER (PARTITION BY clamped_time ORDER BY [Time])
       , rn2 = ROW_NUMBER() OVER (PARTITION BY clamped_time ORDER BY [Time] DESC)
  FROM t
  CROSS APPLY (
      SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, 0, time) / 5 * 5, 0)
  ) AS x(clamped_time)
)
SELECT clamped_time
     , MIN(CASE WHEN rn1 = 1 THEN [Open] END) AS [Open]
     , MIN(CASE WHEN rn2 = 1 THEN [Close] END) AS [Close]
     , MAX([High]) AS [High]
     , MIN([Low]) AS [Low]
     , AVG([Volume])
FROM cte
GROUP BY clamped_time

Демонстрация по дб <> fiddle

2 голосов
/ 04 ноября 2019

Вы хотите анализировать данные с интервалами в 5 минут. Вы можете использовать оконные функции со следующим предложением разделения:

partition by datepart(year, t.[time]),
    datepart(month, t.[time]),
    datepart(day, t.[time]),
    datepart(hour, t.[time]),
    (datepart(minute, t.[time]) / 5)

Запрос:

select *
from (
    select  
        t.time,
        row_number() over(
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
            order by [time]
        ) [rn],
        first_value([open]) over(
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
            order by [time]
        ) [open],
        last_value([close]) over(
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
            order by [time]
        ) [close],
        max([high]) over (
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
        ) [high],
        min([low]) over (
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
        ) [low],
        avg([volume]) over (
            partition by datepart(year, [time]),
                datepart(month, [time]),
                datepart(day, [time]),
                datepart(hour, [time]),
                (datepart(minute, [time]) / 5)
        ) [volume]
    from mytable t
) t
where rn = 1
0 голосов
/ 04 ноября 2019

Вы можете попробовать это.

  SELECT
      MIN([Time]) [Time], 
      Min([Open]) [Open],
      LEAD(Min([Open])) OVER (ORDER BY MIN([Time])) AS [Close],
      Max([High]) [High], 
      Min([Low]) [Low], 
      Avg(Volume) Volume
  FROM SampleData
  GROUP BY DATEADD(Minute, -1* DATEPART(Minute, Time) %5, Time)

sql fiddle

...