我有一个由时间戳值和绝对值(米)组成的数据集。有时仪表值会重置为零,这意味着我必须迭代并逐个计算一个增量,然后将其相加以获得给定时间段内的总和。
例如:
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服务器,并接受任何建议。
现在,我正在使用游标执行此操作,检查delta是否为负数,然后将其合计:
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 studio中使用'show execution plan'一起运行时,相对成本是100%(有7或8次操作 - 大多数是聚集索引扫描时间戳),光标为0%(有3个操作)。
(为简单起见,我在这里没有展示的是我有几组不同的数字,在这个表中也有一个外键 - 所以总有一个WHERE子句限制到一个特定的集合。我有几个我一次为几个集合计算给定时间段内这些总计的地方,因此它变成了性能瓶颈。非游标方法也可以很容易地修改为GROUP BY键并立即返回所有集合 - 但是这实际上在我的测试中比多次运行游标更慢,因为除了总体上更慢之外,还有GROUP BY和SUM()操作的额外开销。)
答案 0 :(得分: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,如指定的那样。
答案 1 :(得分: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&gt; n2.value”。
而且......确保我把它们放在正确的位置......我刚刚将它从-1改为+1,因为我认为我已将它翻转过来。
容易!
罗布
答案 2 :(得分: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