2009-08-13 56 views
1

我有一个由时间戳值和绝对(米)值组成的数据集。有时仪表值重置为零,这意味着我必须迭代并逐个计算增量,然后将其累加以获得给定周期的总和。计算连续行之间的delta值的有效方法?

例如:

Timestamp  Value 
2009-01-01  100 
2009-01-02  105 
2009-01-03  120 
2009-01-04  0 
2009-01-05  9 

这里总为29,计算公式为:

(105 - 100) + (120 - 105) + (0) + (9 - 0) = 29 

我使用MS-SQL服务器对于这一点,并开放给任何建议。

现在,我使用的是光标要做到这一点,它会检​​查增量不为负,然后合计起来:

DECLARE CURSOR curTest CURSOR FAST_FORWARD FOR 
    SELECT value FROM table ORDER BY timestamp 
OPEN curTest 
DECLARE @delta bigint, @current bigint, @last bigint 
SET @delta = 0 

FETCH curTest INTO @current 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    IF (@current IS NOT NULL) AND (@current > 0) 
    BEGIN 
     IF (@last IS NOT NULL) AND (@current > @last) 
      SET @delta = @delta + (@current - @last) 
     SET @last = @current 

     FETCH curTest INTO @current 
    END 
END 

CLOSE curTest 
DEALLOCATE curTest 

这将是很好得到一个数据集的像:

Timestamp  Value LastValue 
2009-01-01  100  NULL 
2009-01-02  105  100 
2009-01-03  120  105 
2009-01-04  0  120 
2009-01-05  9  0 

因为这将很容易抓住变化,为(Value> LastValue)过滤,并做一个SUM()。

我想:

SELECT m1.timestamp, m1.value, 
    (SELECT TOP 1 m2.value FROM table WHERE m2.timestamp < m1.timestamp ORDER BY m2.timestamp DESC) as LastValue 
FROM table 

但其实这原来是比光标较慢:当我一起在SQL工作室与“节目的执行计划”中运行这些,这样做的相对成本100% (使用7或8次操作 - 时间戳中聚簇索引扫描的大多数),并且游标为0%(有3次操作)。 (我在这里没有简单展示的是我有几个不同的数字集合,在这个表格中还有一个外键 - 所以总是有一个WHERE子句限制到一个特定的集合。有几个地方我一次计算给定时间段内的这些总数,因此它变成了相当的性能瓶颈,非游标方法也可以很容易地修改为GROUP BY键,并一次返回所有集合 - 但是在我的测试中,这实际上比运行光标多次要慢,因为除了整体速度较慢外,还有额外的GROUP BY和SUM()操作开销。)

回答

4

许多相同...

create table #temp ([timestamp] date,value int); 
insert into #temp (timestamp,value) values ('2009-01-01',100) 
insert into #temp (timestamp,value) values ('2009-01-02',105) 
insert into #temp (timestamp,value) values ('2009-01-03',120) 
insert into #temp (timestamp,value) values ('2009-01-04',0) 
insert into #temp (timestamp,value) values ('2009-01-05',9); 

with numbered as 
(
    select ROW_NUMBER() over (order by timestamp) id,value from #temp 
) 
select sum(n1.value-n2.value) from numbered n1 join numbered n2 on n1.id=n2.id+1 where n1.value!=0 

drop table #temp; 

结果是29,按规定。

+0

1为ROW_NUMBER的使用来计算() – 2009-08-13 04:19:45

2

以row_number开头,然后回到自己身边。

with numbered as 
(
SELECT value, row_number() over (order by timestamp) as Rownum 
FROM table 
) 
select sum(n1.value - n2.value) 
from numbered n1 
    join 
    numbered n2 on n1.Rownum = n2.Rownum +1 

其实......你只需要拿起增加...所以把在WHERE子句,称 “WHERE n1.value> n2.value”。

并确保我已经把它们放在正确的位置......我刚刚将它从-1更改为+1,因为我认为我已将其翻转。

简单!

罗布

0

算法中有太多不必要的连接。

计算每个仪表读数与其后续读数之间的差异是浪费资源。作为一个真实世界的例子,想象一下,如果我的电力公司每天都读我的电表,看看我使用了多少电量,并且总结每日价值以确定我的月总计 - 这只是没有意义。他们只是根据起始值和最终值确定总数!

只需计算第一个读数和最后一个读数之间的差异,并根据“重置”进行调整。你的公式简单地变成:

total value = (final value) - (initial value) 
       + (miscellaneous reductions in value, i.e. resets) 
total value = (9) - (100) + (120) 
      = 29 

找到最终值和初始值是微不足道的。只需找到“重置”过程中“计量器”减少的总量,并将其添加到总量中即可。除非测量记录的重置记录更多,否则这将始终更有效。

要从花钱的溶液借用,“复位”值可通过

create table... 

select sum(n1.value-n2.value) from numbered n1 join numbered n2 
    on n1.id=n2.id+1 where n1.value=0 //note value=0 rather than value!=0