SQL Server 获取构成聚合的第一行和最后一行

Posted

技术标签:

【中文标题】SQL Server 获取构成聚合的第一行和最后一行【英文标题】:SQL Server Getting First & Last Row that makes up aggregation 【发布时间】:2011-11-22 02:39:20 【问题描述】:

我有一个存储一分钟内价格变动数据的表格。每条记录包含最后一分钟的开盘价、最高价、最低价、收盘价和成交量

CREATE TABLE TimeBar (
  Instrument varchar(20),
  BarTimeStamp datetimeoffset(7),
  Open decimal(18, 5),
  High decimal(18, 5),
  Low decimal(18, 5),
  Close decimal(18, 5),
  Volume int
)

我要做的是创建一个查询,我可以在其中将数据聚合到更高的时间范围内,例如,我希望能够显示每个小时的开盘价、最高价、最低价和收盘价。

以下是我目前的查询,我已经设法获得了最高价和最低价,但是您如何获得开盘价和收盘价?

  SELECT MIN(BarTimeStamp) AS TimeStamp,
         MAX(High) AS High,
         MIN(Low) AS Low,
         SUM(Volume) AS Volume
    FROM TimeBar
   WHERE Instrument = 'XYZ'
GROUP BY DATEPART(YEAR, BarTimeStamp), DATEPART(MONTH, BarTimeStamp), DATEPART(DAY, BarTimeStamp), DATEPART(HOUR, BarTimeStamp)

【问题讨论】:

【参考方案1】:

将主查询用作子查询,为每条记录获取最小时间戳和最大时间戳,因此为它们获取各自的开盘价和收盘价。

SELECT x.*, TI.Open, TE.Close
FROM
(
SELECT   Instrument,
         MIN(BarTimeStamp) AS TimeStampIni,
         MAX(BarTimeStamp) AS TimeStampEnd,
         MAX(High) AS High,
         MIN(Low) AS Low,
         SUM(Volume) AS Volume
    FROM TimeBar
GROUP BY Instrument, DATEPART(YEAR, BarTimeStamp), DATEPART(MONTH, BarTimeStamp), DATEPART(DAY, BarTimeStamp), DATEPART(HOUR, BarTimeStamp)
) x
inner join TimeBar TI on ti.Instrument = x.Instrument AND TI.BarTimeStamp = x.TimeStampIni
inner join TimeBar Te on te.Instrument = x.Instrument AND Te.BarTimeStamp = x.TimeStampEnd

【讨论】:

谢谢,正是我想要的。【参考方案2】:

首先要做的是按时间范围分组:

SELECT MIN(BarTimeStamp) AS StartTimeStamp,
         MAX(BarTimeStamp) AS EndTimeStamp
    FROM #TimeBar
   WHERE Instrument = 'TEST'
GROUP BY DATEPART(YEAR, BarTimeStamp), DATEPART(MONTH, BarTimeStamp), DATEPART(DAY,       BarTimeStamp), DATEPART(HOUR, BarTimeStamp)

之后,问题是:

进行连接以获取范围, 进行连接以开始 进行连接以获得最小值 按原始范围分组以选择所有内容

我喜欢使用公用表表达式来简化:

;WITH times as (
    SELECT MIN(BarTimeStamp) AS StartTimeStamp,
             MAX(BarTimeStamp) AS EndTimeStamp
        FROM #TimeBar
       WHERE Instrument = 'TEST'
    GROUP BY DATEPART(YEAR, BarTimeStamp), DATEPART(MONTH, BarTimeStamp), DATEPART(DAY, BarTimeStamp), DATEPART(HOUR, BarTimeStamp)
)
SELECT 
    StartTimeStamp as TimeStamp
    ,MIN([first].[Open]) as [Open]
    ,MAX(ranged.High) as High
    ,MAX(ranged.Low) as Low
    ,MIN([last].[Close]) as [Close]
    ,SUM(ranged.Volume) as Volume
FROM times 
INNER JOIN #TimeBar ranged ON times.StartTimeStamp <= ranged.BarTimeStamp AND times.EndTimeStamp >= ranged.BarTimeStamp
INNER JOIN #TimeBar [first] ON times.StartTimeStamp = [first].BarTimeStamp 
INNER JOIN #TimeBar [last] ON times.EndTimeStamp = [last].BarTimeStamp 
GROUP BY [times].StartTimeStamp

这是我的测试数据:

CREATE TABLE #TimeBar (
  Instrument varchar(20),
  BarTimeStamp datetimeoffset(7),
  [Open] decimal(18, 5),
  High decimal(18, 5),
  Low decimal(18, 5),
  [Close] decimal(18, 5),
  Volume int
)

insert into #TimeBar values ('TEST', '2011-11-21 1:00', 5, 6, 4, 8, 100)
insert into #TimeBar values ('TEST', '2011-11-21 1:10', 1, 7, 3, 4, 100)
insert into #TimeBar values ('TEST', '2011-11-21 2:10', 15, 16, 17, 18, 100)
insert into #TimeBar values ('TEST', '2011-11-21 2:30', 12, 16, 17, 19, 100)
insert into #TimeBar values ('TEST', '2011-11-21 2:50', 13, 14, 15, 20, 100)

结果是:

TimeStamp                          Open                                    High                                    Low                                     Close                                   Volume
---------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- -----------
2011-11-21 01:00:00.0000000 +00:00 5.00000                                 7.00000                                 4.00000                                 4.00000                                 200
2011-11-21 02:10:00.0000000 +00:00 15.00000                                16.00000                                17.00000                                20.00000                                300

注意,我为此使用了一个临时表,只需将 #TimeBar 更改为 TimeBar 即可为您的真实表更改它。


除此之外,我不喜欢将Instrurment varchar 视为字段定义。您应该为此使用surrogate key。

【讨论】:

以上是关于SQL Server 获取构成聚合的第一行和最后一行的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005获取任何年份中任何月份的第一个和最后一个日期

SQL Server学习记录之获取每月每季度每年第一天和最后一天

如何从SQL Server中的重复行中获取最后一行?

如何使用聚合函数在 MySQL 查询中获取分组记录的第一条和最后一条记录?

如何使用聚合函数在 MySQL 查询中获取分组记录的第一条和最后一条记录?

获取本月、本季、本年第一天和最后一天等等sql(sql server)