SQL Server:按日期时间和first_value分组

时间:2015-05-05 04:08:25

标签: sql-server azure

我在SQL Azure数据库中有一段时间的汇率表。

如:

  1. USD-AUD, 2015-1-1 00:01, 0.70 
     2. USD-AUD, 2015-1-1 00:02, 0.73  
     3. USD-AUD, 2015-1-1 00:03, 0.69  
     4. USD-AUD, 2015-1-1 00:04, 0.78  
     5. USD-AUD, 2015-1-1 00:05, 0.75
     6. USD-AUD, 2015-1-1 00:06, 0.80

(Pair, DateTime, ExchangeRate)

我想以5分钟的间隔进行分组,并显示最小速率,最大速率以及第一个(第1行)和最后一个(第5行)。因此开放将是0.70,高= 0.78,低= 0.69并且接近0.75

我已经在这里关注了如何分组哪些有用的线索,但我无法弄清楚如何获得"打开"和"关闭"

Select min(price) as low, max(price) as high
from exchangetable
(GROUP BY DATEPART(YEAR, [Time]),
    DATEPART(MONTH, [Time]),
    DATEPART(DAY, [Time]),
    DATEPART(HOUR, [Time])
    GROUP BY DATEPART(mi, [Time]) % 5

使用FIRST_VALUE http://www.mssqltips.com/sqlservertip/2640/sql-server-2012-functions--firstvalue-and-lastvalue/我得到了#34;此版本的SQL错误不支持"

解决方案是加入自身还是游标?

3 个答案:

答案 0 :(得分:1)

不需要游标。请尝试以下方法。请注意,我必须更改不一致的列名称。您还需要GROUP BY中的整数除法而不是模数运算符(%)。 “打开”和“关闭”列由子选择生成。

SELECT
  DATEPART(YEAR,  e1.[datetime]) AS Year,
  DATEPART(MONTH, e1.[datetime]) AS Month,
  DATEPART(DAY,   e1.[datetime]) AS Day,
  DATEPART(HOUR,  e1.[datetime]) AS Hour,
  DATEPART(mi,    e1.[datetime])/5 AS TimeBracket,
  (
    SELECT TOP 1 
      e2.ExchangeRate 
     FROM 
       exchangetable e2 
     WHERE 
       DATEPART(YEAR,  e1.[datetime])   = DATEPART(YEAR,  e2.[datetime]) AND
       DATEPART(MONTH, e1.[datetime])   = DATEPART(MONTH, e2.[datetime]) AND
       DATEPART(DAY,   e1.[datetime])   = DATEPART(DAY,   e2.[datetime]) AND
       DATEPART(HOUR,  e1.[datetime])   = DATEPART(HOUR,  e2.[datetime]) AND
       DATEPART(MI,    e1.[datetime])/5 = DATEPART(MI,    e2.[datetime])/5
     ORDER BY
       [datetime]     
  ) AS [Open],
  (
    SELECT TOP 1 
      e2.ExchangeRate 
     FROM 
       exchangetable e2 
     WHERE 
       DATEPART(YEAR,  e1.[datetime])   = DATEPART(YEAR,  e2.[datetime]) AND
       DATEPART(MONTH, e1.[datetime])   = DATEPART(MONTH, e2.[datetime]) AND
       DATEPART(DAY,   e1.[datetime])   = DATEPART(DAY,   e2.[datetime]) AND
       DATEPART(HOUR,  e1.[datetime])   = DATEPART(HOUR,  e2.[datetime]) AND
       DATEPART(MI,    e1.[datetime])/5 = DATEPART(MI,    DATEADD(MI, -1, e2.[datetime]))/5
     ORDER BY
       [datetime] DESC
  ) AS [Close],
  MIN(ExchangeRate) AS low,
  MAX(ExchangeRate) AS high
FROM
  exchangetable e1
GROUP BY 
  DATEPART(YEAR,  [datetime]),
  DATEPART(MONTH, [datetime]),
  DATEPART(DAY,   [datetime]),
  DATEPART(HOUR,  [datetime]),
  DATEPART(mi,    [datetime])/5

哦和BTW我需要添加一个小时的日期部分,否则你会发现" TimeBrackets"从一天的不同部分聚集在一起。您的数据使用此查询生成以下结果:

Year    Month   Day Hour    TimeBracket Open    Close   low    high
2015    1        1    0     0           0.70    0.75    0.69    0.78
2015    1        1    0     1           0.75    0.80    0.75    0.80

答案 1 :(得分:0)

您可以将DENSE_RANK()ROW_NUMBER()PARTITION BY一起使用,以获得您想要的内容。这样的事情。

代码说明:

  • DENSE_RANK()根据当前日期,小时和分钟/ 5分配不同的Grp。

  • 1st ROW_NUMBER根据增加的时间从1开始在grp内分配唯一的数字。如果此数字为1,则该行是grp

  • 中的第一行
  • 第2个ROW_NUMBER根据减少的时间从1开始在grp内分配唯一的数字。如果此数字为1,则该行是grp

  • 中的最后一行

<强>查询

;WITH exchangetable(Pair, [Time], Price) AS (
SELECT 'USD-AUD', '2015-1-1 00:01', 0.70 
UNION ALL SELECT'USD-AUD', '2015-1-1 00:02', 0.73  
UNION ALL SELECT'USD-AUD', '2015-1-1 00:03', 0.69  
UNION ALL SELECT'USD-AUD', '2015-1-1 00:04', 0.78  
UNION ALL SELECT'USD-AUD', '2015-1-1 00:05', 0.75
UNION ALL SELECT'USD-AUD', '2015-1-1 00:06', 0.80
), Grp as 
(
Select
DENSE_RANK()OVER(ORDER BY CONVERT(DATE,[Time]),DATEPART(HOUR, [Time]),(DATEPART(mi, [Time]) / 5)) grp,
ROW_NUMBER()OVER(PARTITION BY CONVERT(DATE,[Time]),DATEPART(HOUR, [Time]),(DATEPART(mi, [Time]) / 5) ORDER BY [Time]) f_row,
ROW_NUMBER()OVER(PARTITION BY CONVERT(DATE,[Time]),DATEPART(HOUR, [Time]),(DATEPART(mi, [Time]) / 5) ORDER BY [Time] DESC) l_row,*
from exchangetable
)
SELECT G1.*,G_First.*,G_Last.*
FROM 
(
    SELECT grp,MAX(Price) max_price,MIN(Price) min_price
    FROM Grp
    GROUP BY grp
) G1
INNER JOIN Grp G_Last ON G_Last.grp = G1.grp AND G_Last.l_row = 1
INNER JOIN Grp G_First ON G_First.grp = G1.grp AND G_First.f_row = 1

答案 2 :(得分:0)

避免连接的另一种方法是使用

DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [exchangetable].[Time]) / 5 * 5, 0)

作为我们的窗口期,然后在分析函数上使用SELECT DISTINCT而不是GROUP BY,因为我们通过[Time]对项目进行排序,因此我们不想将其作为分组的一部分。

;WITH [exchangetable] ([Pair], [Time], [Price]) AS (
    SELECT
        'USD-AUD',
        '2015-1-1 00:01',
        0.70
    UNION ALL
    SELECT
        'USD-AUD',
        '2015-1-1 00:02',
        0.73
    UNION ALL
    SELECT
        'USD-AUD',
        '2015-1-1 00:03',
        0.69
    UNION ALL
    SELECT
        'USD-AUD',
        '2015-1-1 00:04',
        0.78
    UNION ALL
    SELECT
        'USD-AUD',
        '2015-1-1 00:05',
        0.75
    UNION ALL
    SELECT
        'USD-AUD',
        '2015-1-1 00:06',
        0.80
),
      [periods] AS (
    SELECT
        [PeriodNo] = DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [exchangetable].[Time]) / 5 * 5, 0),
        [exchangetable].[Pair],
        [exchangetable].[Time],
        [exchangetable].[Price]
    FROM [exchangetable]
)
SELECT DISTINCT
       [periods].[Pair],
       [periods].[PeriodNo],
       [StartTime] = FIRST_VALUE([periods].[Time]) OVER (PARTITION BY
                                                             [periods].[Pair],
                                                             [periods].[PeriodNo]
                                                         ORDER BY [periods].[Time]
                                                   ),
       [EndTime] = LAST_VALUE([periods].[Time]) OVER (PARTITION BY
                                                          [periods].[Pair],
                                                          [periods].[PeriodNo]
                                                      ORDER BY [periods].[Time]
                                                      ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
                                                ),
       [StartVal] = FIRST_VALUE([periods].[Price]) OVER (PARTITION BY
                                                             [periods].[Pair],
                                                             [periods].[PeriodNo]
                                                         ORDER BY [periods].[Time]
                                                   ),
       [EndVal] = LAST_VALUE([periods].[Price]) OVER (PARTITION BY
                                                          [periods].[Pair],
                                                          [periods].[PeriodNo]
                                                      ORDER BY [periods].[Time]
                                                      ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
                                                )
FROM [periods];