按小时按大型数据集分组


12

我使用MS SQL 2008从250万条记录中选择一个平均字段。每条记录代表一秒钟。MyField是这些1秒记录的每小时平均值。当然,服务器CPU达到100%,选择时间太长。我可能需要保存这些平均值,以便SQL不必在每个请求中都选择所有这些记录。该怎么办?

  SELECT DISTINCT
         CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR)
ORDER BY TimeStamp

6
时间戳记是聚集索引的一部分吗?应该是...

@antisanity-为什么?他正在最大化CPU而不是磁盘io
杰克说请尝试topanswers.xyz 2011年

Answers:


5

查询的一部分是使CPU长时间处于最大化状态,这是GROUP BY子句中的功能以及在这种情况下分组总是需要未索引排序的事实。虽然时间戳字段上的索引将有助于初始过滤器,但必须在过滤器匹配的每一行上执行此操作。加快此步伐将使用更有效的途径来完成Alex所建议的相同工作,但您仍然会遇到很大的效率低下,因为使用查询计划程序的任何功能组合都无法提出它可以通过任何索引得到帮助,因此它必须遍历每一行,然后首先运行函数以计算分组值,然后才可以对数据进行排序并计算所得分组的汇总。

因此,解决方案是通过某种方式使进程组可以使用索引,或者以其他方式消除立即考虑所有匹配行的需要。

您可以为每行维护一个额外的列,其中包含将时间舍入为小时的时间,并为该列编制索引以用于此类查询。这会使您的数据不规范,因此可能会感觉“脏”,但是它比缓存所有聚合以供将来使用(并在更改基本数据时更新该缓存)更有效。额外的列应由触发器维护或作为持久化的计算列,而不是由其他地方的逻辑维护,因为这将保证所有当前和将来可能插入数据或更新时间戳列的位置,或者现有行会在新数据中产生一致的数据柱。您仍然可以获取MIN(时间戳)。查询将以这种方式导致的结果仍然是遍历所有行(显然,这是无法避免的),但是它可以对索引进行排序,在到达分组中的下一个值时,为每个分组输出一行,而不必在执行分组/聚合之前为未索引的排序操作记住整个行集。它也将使用更少的内存,因为它不需要记住先前分组值中的任何行即可处理它正在查看的行或其余行。

该方法消除了对整个结果集在内存中某个地方的查找需求,并对组操作进行了未索引的排序,并将组值的计算从大型查询中删除(将作业移至产生结果的单个INSERT / UPDATE中)。数据),并且应允许此类查询以可接受的方式运行,而无需维护汇总结果的单独存储。

一种没有的方法对数据进行非规范化,但仍需要额外的结构,是使用“时间表”,在这种情况下,您可能考虑的所有时间都每小时包含一行。该表不会占用DB或可观大小的大量空间-可以覆盖一张包含两个日期的一行(小时的开始和结束,例如'2011-01-01 @)的表的100年时间跨度00:00:00.0000','2011-01-01 @ 00:00:59.9997',“ 9997”是DATETIME字段不会舍入到下一秒的最小毫秒数),这两者都是集群主键将占用约14Mb的空间(每行8 + 8字节* 24小时/天* 365.25天/年* 100,加上集群索引树结构的开销,但是开销不会很大) 。

SELECT CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour
     , MIN([timestamp]) as TimeStamp
     , AVG(MyField) As AvgField
FROM TimeRangeByHours tt
INNER JOIN MyData md ON md.TimeStamp BETWEEN tt.StartTime AND tt.EndTime
WHERE tt.StartTime > '4/10/2011'
GROUP BY tt.StartTime
ORDER BY tt.StartTime

这意味着查询计划者可以安排使用MyData.TimeStamp上的索引。查询计划程序应该足够聪明,可以与MyData.TimeStamp上的索引一起步入驯服表,再次对每个分组输出一行,并在到达下一个分组值时丢弃每组或每行。不将所有中间行存储在RAM中的某个位置,然后对它们执行未索引的排序。当然,此方法要求您创建时间表,并确保它前后跨得足够远,但是您可以将时间表用于对不同查询中许多日期字段的查询,其中“额外列”选项将需要您需要通过这种方式对每个日期字段进行过滤/分组的额外计算列,以及表格的小尺寸(除非您需要跨度为10,

与您当前的情况和所计算的列解决方案相比,时间表方法有一个额外的差异(这可能是非常有利的):只需更改上面示例查询中的INNER JOIN,就可以返回没有数据的期间的行成为外面的一员。

有人建议没有物理时间表,而总是从表返回函数中返回它。这意味着时间表的内容永远不会存储在磁盘上(或需要从磁盘上读取),并且如果函数编写得当,您就不必担心时间表需要在时间上来回移动多长时间。怀疑为每个查询生成一些行的内存表的CPU成本值得节省一下创建物理时间表的麻烦(如果需要维护,如果其时间跨度要超出初始版本的限制,则可以节省一些开销)。

注意:您在原始查询中也不需要DISTINCT子句。分组将确保这些查询在所考虑的每个时间段内仅返回一行,因此DISTINCT只会做更多事情,而只会旋转CPU多一点(除非查询计划者注意到,distinct是无操作的,否则它将忽略它,不占用额外的CPU时间)。


3

请参阅此问题(预定日期)。此外,为什么还要将所有内容都转换为字符串-您以后可以(如果需要)这样做。

  SELECT DISTINCT
         dateadd(hour,datediff(hour,0,[timestamp]),0) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY dateadd(hour,datediff(hour,0,[timestamp],0);
ORDER BY TimeStamp

1

您是想使查询更快还是在询问如何制作数据快照并保存它?

如果要使其更快,则肯定需要在TimeStamp字段上建立索引。另外,我建议使用此方法将其转换为小时:

select convert(varchar(13), getdate(), 121)

如果您需要制作快照并在以后重复使用insert into,请使用查询结果创建一个新表。根据索引表并使用它。据我了解,您将需要有关TimeStampHour的索引。

您还可以设置一个工作,以在新的汇总表中汇总每日数据。


-1

通过将group by子句转换成这样的字符串,实质上就是使它成为数据库中每一行的未索引命中。这就是杀害您的表现。如果索引使用正确,那么任何中途服务器都将能够处理像一百万条记录那样的简单聚合。我会修改您的查询,并在时间戳上添加聚簇索引。这将解决您的性能问题,而每小时计算数据只是推迟了问题。


1
-1-不,您不是在“对数据库中的每一行都进行无索引命中”-上的任何索引TimeStamp仍将用于过滤行
杰克说,请尝试topanswers.xyz 2011年

-3

我会考虑放弃使用关系数据库模型实现这种计算的想法。尤其是如果您有许多数据点每秒要收集值。

如果您有钱,可以考虑购买专门的过程数据历史学家,例如:

  1. 霍尼韦尔统一PHD
  2. Osisoft PI
  3. Aspentech IP21
  4. 等等

这些产品可以存储大量异常密集的时间序列数据(以专有格式),同时允许快速处理数据提取查询。查询可以指定许多数据点(也称为标签),较长的时间间隔(月/年),并且可以另外进行各种各样的汇总数据计算(包括平均值)。

..并注意:DISTINCT在编写SQL时,我总是尽量避免使用关键字。这绝不是一个好主意。在您的情况下,应该可以DISTINCT通过添加MIN([timestamp])GROUP BY子句中来删除并获得相同的结果。


1
这不是很准确。关系数据库非常适合250万条记录。而且他甚至没有跨很多表进行联接。您需要对数据进行非规范化或迁移到非关系系统的第一个迹象是,当您在多个表之间进行大型,复杂的联接时。张贴者的数据集实际上听起来像是对关系数据库系统的完全可接受的使用。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.