【问题标题】:Data Aggregation in SQL Server 2005SQL Server 2005 中的数据聚合
【发布时间】:2010-10-18 12:29:44
【问题描述】:

我需要查询 SQl server 2005 (SQL server management studio express)。 我将数据存储为 1 分钟时间范围(每行 1 分钟),每个表的列是 ID、符号、日期时间、开盘价、最高价、最低价、收盘价、成交量。 我需要转换(压缩)到每个可能的多个时间帧,所以假设 10 分钟、13 分钟、15 分钟等等。 如果有人可以提供帮助,请提供完整的详细信息。 谢谢 阿尔贝托

【问题讨论】:

  • 这不只是一个 GROUP BY 子句吗?
  • 很抱歉,如果这个问题超出了本组的规则,我将删除我的请求。
  • 我真的不明白你所说的压缩“到每个可能的多个时间范围,所以让我们说 10 分钟、13、15 等等”的意思。你能提供示例数据和期望的结果吗?
  • 有足够代表的人也应该修复问题标题。我以为是关于 SQL Server 中的实际数据压缩。
  • 是的 Martin:我将财务数据存储为每行 1 分钟的数据。假设第一行,9:00:00。这一行的范围从 9:00:00 到 9:00:59。记录的数据是 Open(第一个交易值)High(此时间范围的最大值)Low(最低值交易)收盘(此时间间隔内的最后价格)交易量(所有交易的总和)。

标签: sql sql-server sql-server-2005 data-compression


【解决方案1】:

Alberto,您似乎需要在 SQL 语句中使用“Group By”子句(如 Leppie 所述)。所以,你最好去寻找它。

首先,您应该使用开始和结束日期/时间过滤要聚合的行,然后按上述子句对它们进行分组。

这是我通过 Google 搜索“sql group by”关键字时的第一个 link

【讨论】:

    【解决方案2】:
    ;WITH cte AS
    (SELECT *,
            (32 * CAST([DATETIME] AS INT)) + DATEPART(HOUR,[DATETIME]) + (DATEPART(MINUTE,[DATETIME])/15)/4.0 AS Seg
         FROM     prices
         )
    ,cte1 AS
    (
    SELECT *,
            ROW_NUMBER() OVER (PARTITION BY Symbol,Seg ORDER BY [DATETIME])      AS RN_ASC ,
            ROW_NUMBER() OVER (PARTITION BY Symbol,Seg ORDER BY [DATETIME] DESC) AS RN_DESC
    FROM cte
    )     
    SELECT 
          Symbol,
          Seg,
          MAX(CASE WHEN RN_ASC=1 THEN [DATETIME] END) AS OpenDateTime,
          MAX(CASE WHEN RN_ASC=1 THEN [OPEN] END) AS [OPEN],
          MAX(High) High,
          MIN(Low)  Low,
          SUM(Volume) Volume,
          MAX(CASE WHEN RN_DESC=1 THEN [CLOSE] END) AS [CLOSE],
          MAX(CASE WHEN RN_DESC=1 THEN [DATETIME] END) AS CloseDateTime
    FROM cte1
    GROUP BY Symbol,Seg
    ORDER BY OpenDateTime
    

    或者另一种可能值得测试的方法,看看它是否更快。

    DECLARE @D1 DATETIME
    DECLARE @D2 DATETIME
    DECLARE @Interval FLOAT
    
    SET @D1  = '2010-10-18 09:00:00.000'
    SET @D2  = '2010-10-19 18:00:00.000'
    SET @Interval = 15
    
    ;WITH 
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),
    Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4),
    Ranges AS(
    SELECT 
          DATEADD(MINUTE,@Interval*(i-1),@D1) AS StartRange,
          DATEADD(MINUTE,@Interval*i,@D1) AS NextRange
    FROM Nums where i <= 1+CEILING(DATEDIFF(MINUTE,@D1,@D2)/@Interval))
    ,cte AS (
    SELECT 
         * 
         ,ROW_NUMBER() OVER (PARTITION BY Symbol,r.StartRange ORDER BY [DateTime])      AS RN_ASC 
         ,ROW_NUMBER() OVER (PARTITION BY Symbol,r.StartRange ORDER BY [DateTime] DESC) AS RN_DESC
    FROM Ranges r
    JOIN prices p ON p.[DateTime] >= r.StartRange and p.[DateTime] < r.NextRange )
    SELECT 
          Symbol,
          MAX(CASE WHEN RN_ASC=1 THEN [DateTime] END) AS OpenDateTime,
          MAX(CASE WHEN RN_ASC=1 THEN [Open] END) AS [Open],
          MAX(High) High,
          MIN(Low)  Low,
          SUM(Volume) Volume,
          MAX(CASE WHEN RN_DESC=1 THEN [Close] END) AS [Close],
          MAX(CASE WHEN RN_DESC=1 THEN [DateTime] END) AS CloseDateTime
    FROM cte
    GROUP BY Symbol,StartRange
    ORDER BY OpenDateTime
    

    【讨论】:

    • 谢谢马丁,但我收到错误:也许我可以从我的 SQL 数据库中为您提供一些示例 xls 数据,我可以想象在没有数据的情况下很难正确编码此查询。我可以在这里附加文件吗?否则,acepsut 是我的 Skype 昵称以及我的 gmail.com 帐户
    • 你得到什么错误? (如果您想将数据放在某个地方,也许 Google 电子表格会是个好地方?)
    • @Alberto - 这是在 smirkingman 的回答下面的 cmets 之后更新的。
    • 嗨 Martin,您编辑的版本正在运行,但在用于聚合表 img404.imageshack.us/img404/5096/f996.png 2010-10-19 中的所有数据时跳过了一些行,错过了从 9:00 到 16:44 的所有数据跨度>
    • @Alberto 该符号WHERE [DateTime] &gt;= '2010-10-18T17:30:00.000' AND [DateTime] &lt; '2010-10-19T16:45:00.000' 的原始数据是什么样的?
    【解决方案3】:

    不是简单的“分组依据” - 需要为组中的第一行和相应的最后一行获取打开和关闭值。或者至少对于外汇数据来说是这样的:)

    【讨论】:

      【解决方案4】:

      使用存储过程先提取 MIN(datetime) 会更漂亮,但这里有一个草图:

      WITH quarters(q) AS (
          SELECT DISTINCT
              15*CAST(DATEDIFF("n",'2000/01/01',dataora) / 15 as Int) AS primo
          FROM 
              Prezzi
      )
      SELECT
          simbolo, DATEADD("n",q,'2000/01/01') AS tick, 
              MIN(minimo) AS minimo, MAX(massimo) AS massimo,
              (SELECT 
                  TOP 1 apertura FROM Prezzi P 
               WHERE 
                  P.simbolo = simbolo AND 
                  P.dataora >= DATEADD("n",q,'2000/01/01')
               ORDER BY
                  P.dataora ASC
               ) as primaapertura,
              (SELECT 
                  TOP 1 chiusura FROM Prezzi P 
               WHERE 
                  P.simbolo = simbolo AND 
                  P.dataora < DATEADD("s",14*60+59,DATEADD("n",q,'2000/01/01'))
               ORDER BY
                  P.dataora DESC
               ) as ultimachiusara,
              SUM(volume) / COUNT(*) AS volumemedio
      FROM
          quarters INNER JOIN Prezzi
          ON dataora BETWEEN DATEADD("n",q,'2000/01/01')
              AND DATEADD("s",14*60+59,DATEADD("n",q,'2000/01/01'))
      GROUP BY
          simbolo, DATEADD("n",q,'2000/01/01')
      ORDER BY 
          1, 2
      

      WITH 子句在您的数据集中获取 15 分钟间隔的列表,向下取整(假设 2000 年之前没有任何内容)。 然后使用这些间隔按 14:59 间隔分组。 对于数量,您必须决定是要平均还是总数量。

      语法可能有点过时,但你应该明白。

      编辑:调整 MIN(打开),MIN(关闭)以获取 FIRST 和 LAST。实际上这不会有太大变化,因为打开和关闭的概念取决于了解报价来源的交易所与收集数据的计算机时钟之间的时差。

      此外,除非 OP 有权获得所有交易所的实时信息,否则所有报价都会延迟 20 分钟。

      EDIT(2):非常正确,FIRST 和 LAST 是我在 IBM 时代的遗留物 >;-)

      解决方案现在使用 TOP 和 ASC/DESC 在间隔期间选择第一个和最后一个引号。

      【讨论】:

      • 你为什么有MIN(open)MIN(close)? OP 需要与每个段的第一条记录相关的 open 价格和与每个段的最后一条记录相关的 close 价格。
      • 懒惰。他的数据来自价格信息。开盘和收盘在白天不能改变,它们是今天早上第一件事和昨晚最后一件事的股价。
      • 这确实有道理,但这不是 OP 在 cmets 中定义它的方式。 “收盘价(此时间间隔内的最后价格)”@Alberto - 你能澄清一下吗?
      • 大家好,我上传了一张图片来展示数据库的样子img706.imageshack.us/img706/2267/f987.png 有人解释说:交易所从 9:00:00 开始,从 9:00:00 到 9:00 有很多交易:59.所有这些交易所都被称为“报价”。Apertura(开盘)是开市后的第一个交易价格,Chiusura(收盘)是最后一个交易价格,Massimo(高)和 Minimo(低)是最高和最低交易价格在此时间范围内。您在此图片中看到的是刻度聚合或压缩,并存储在 1 分钟的时间范围内。我需要一个查询以每隔 1 分钟多个 t.frame 得到相同的结果
      • 所以对于 15 分钟的数据聚合,我应该有 1 行每 15 1 分钟的行,其中 Open 将是 9:00:00 Open 值,Close 将是 9:14:00 行关闭值,高所有 15 1 分钟行中的最高值 高列,低所有 15 1 分钟行中的最低值 低列,对于交易量,这 15 个 1 分钟行的总和。这适用于所有整个股票,所以第一行将是9:00:00, 然后 9:15:00, 9:30:00 等等直到时间结束(这个市场在 17:30:00 关闭)。我还需要设置时间开始和时间结束,因为美国市场交易时间不同
      【解决方案5】:
          Declare @tbl1MinENI Table 
          (ID int identity,
           Simbolo char(3),
           DataOra datetime,
           Apertura numeric(15,4),
           Massimo  numeric(15,4),
           Minimo numeric(15,4),
           Chiusura numeric(15,4),
           Volume int)
      
          Insert Into  @tbl1MinENI (  Simbolo, DataOra, Apertura, Massimo, Minimo, Chiusura, Volume)
          Values
          ('ENI', '2010/10/18 09:00:00', 16.1100, 16.1800, 16.1100, 16.1400, 244015),
          ('ENI', '2010/10/18 09:01:00', 16.1400, 16.1400, 16.1300, 16.1400, 15692 ),
          ('ENI', '2010/10/18 09:02:00', 16.1400, 16.1500, 16.1400, 16.1500, 147035),
          ('ENI', '2010/10/18 09:03:00', 16.1500, 16.1600, 16.1500, 16.1600, 5181  ),
          ('ENI', '2010/10/18 09:04:00', 16.1600, 16.2000, 16.1600, 16.1900, 5134  ),
          ('ENI', '2010/10/18 09:05:00', 16.1900, 16.1900, 16.1800, 16.1800, 15040 ),
          ('ENI', '2010/10/18 09:06:00', 16.1900, 16.1900, 16.1600, 16.1600, 68867 ),
          ('ENI', '2010/10/18 09:07:00', 16.1600, 16.1600, 16.1600, 16.1600, 7606  ),
          ('ENI', '2010/10/18 09:08:00', 16.1500, 16.1500, 16.1500, 16.1500, 725   ),
          ('ENI', '2010/10/18 09:09:00', 16.1600, 16.1600, 16.1600, 16.1600, 81    ),
          ('ENI', '2010/10/18 09:10:00', 16.1700, 16.1800, 16.1700, 16.1700, 68594 ),
          ('ENI', '2010/10/18 09:11:00', 16.1800, 16.1800, 16.1800, 16.1800, 6619  )
      
          Declare @nRowsPerGroup int = 3
      
      ;With Prepare as
      (
      Select datediff(minute, '2010/10/18 09:00:00', DataOra)/@nRowsPerGroup as Grp,
             Row_Number() over (partition by datediff(minute, '2010/10/18 09:00:00', DataOra)/@nRowsPerGroup order by dataora) as rn,
             *
        From tbl1MinENI     
      ), b as
      (
      Select a.Grp, 
               Min(a.DataOra)          as GroupDataOra, 
               Min(ID) AperturaID,
               max(a.Massimo)          as Massimo, 
               Min(a.Minimo)           as Minimo, 
               max(id) ChiusuraID,
               sum(a.Volume)           as Volume
          From Prepare a
         Group by Grp
      )
      Select b.grp,
             b.GroupDataOra,
             ta.Apertura,
             b.Massimo,
             b.Minimo,
             tc.Chiusura,
             b.Volume
       From b
       Inner Join tbl1MinENI ta on ta.ID=b.AperturaID
       Inner Join tbl1MinENI tc on tc.ID=b.ChiusuraID
       ;   
      

      【讨论】:

      • 谢谢 Nikola,我遇到了一些错误 1) 消息 102 级别 15 第 13 行 ',' 附近的 sintax 不正确 2) 消息 139 级别 15 行不可能将预定义值分配给局部变量 3)消息 137 级别 15 第 30 行声明标量值“@nRowsPerGroup”。
      • 如果 09:00:00 的记录丢失,这将不起作用(参见前面的 cmets)
      • 它有效。只需删除 09:00:00 的插入并尝试。事实上,您可以删除任意数量的行,它仍然可以工作。
      猜你喜欢
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多