2009-08-13 81 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