2011-09-20 138 views
0
之間經過秒除以

您好我有計值的SQL Server數據庫,其中包含與下面列的表的表:找到一個SQL Server查詢中的差異,通過記錄

Timestamp, meterID, rawValue 

我試圖使用查詢和谷歌圖表來繪製水的使用率,問題是我需要計算每15到30分鐘更新一次的原始計量表值的比率。

我想運行一個查詢,返回特定水錶的值。

MeterID, Timestamp, (rawValue-previousRawValue)/(timestamp difference in seconds) 

任何幫助,非常感謝。

+0

您是否使用SQL Server 2000或2005/8? –

回答

0

我對我的查詢和@ Bogdan的查詢做了一些小的更改,使它們儘可能相似,然後進行比較。波格丹的修改後的查詢是在這篇文章的底部。

根據SQL Server查詢執行計劃,在同一個查詢中堆疊在一起,我的查詢成本爲53%,而Bogdan的查詢爲47%。

對於在波格丹的職位提供的數據集:

  • 我的查詢:6次掃描和27邏輯讀取
  • 波格丹的:6次掃描和72邏輯讀取

我附加值每對於meterID 1和2,15secs最多5分鐘,共計42條記錄,然後使用SQL Server Profiler重新查詢查詢。

如果我的查詢在讀取中獲勝,Bogdan仍然贏得​​CPU和持續時間。

  CPU SCANS READS DURATION 
-------------------------------------- 
Mine  47  22 313  249ms 
Bogdan's 16  22 972  15ms 
-------------------------------------- 

我做幾個假設,如您MeterID是INT。根據需要更改。

我還假設你想運行特定儀表ID的查詢,它將作爲參數傳遞給存儲過程。

這應該適用於SQL Server 2005及更高版本。

我做了一些事情,可能會分散實際的解決方案。核心邏輯確實在WHILE循環中。

CREATE PROCEDURE [dbo].[GetMeterResults] 
    @MeterID INT 
AS 
BEGIN 

    -- create a temp table to store results 
    CREATE TABLE #tempResults 
    (
     MeterID INT, 
     [Timestamp] DATETIME, 
     Result FLOAT 
    ) 

    DECLARE 
     @Timestamp DATETIME, 
     @RawValue INT, 
     @LastTimestamp DATETIME, 
     @LastRawValue INT, 
     @FirstRun BIT = 1 

    DECLARE cr CURSOR FAST_FORWARD FOR 
    SELECT 
     [Timestamp], 
     RawValue 
    FROM 
     YourTable 
    WHERE 
     MeterID = @MeterID 
    ORDER BY 
     [Timestamp] 

    OPEN cr 
    FETCH NEXT FROM cr INTO @Timestamp, @RawValue 

    WHILE (@@FETCH_STATUS = 0) 
    BEGIN 
     IF (@FirstRun = 1) 
     BEGIN -- the first run 
      SELECT @FirstRun = 0 -- flip the bit for all future runs 
     END 
     ELSE -- all subsequent runs after the first 
     BEGIN  
      INSERT INTO 
       #tempResults 
      SELECT 
       @MeterID, 
       @Timestamp, 
       (@RawValue - @LastRawValue) * 1.00/DATEDIFF(s, @LastTimestamp, @Timestamp) 
     END 

      -- save the current values for comparison on the next run  
     SELECT 
      @LastTimestamp = @Timestamp, 
      @LastRawValue = @RawValue 

     FETCH NEXT FROM cr INTO @Timestamp, @RawValue 
    END 

    CLOSE CR 
    DEALLOCATE CR 

    -- return the result set 
    SELECT 
     * 
    FROM 
     #tempResults 

    -- clean up the temp table 
    DROP TABLE #tempResults 
END 
GO 

波格丹的修改後的查詢,通過MeterID與我上面的查詢一個蘋果對蘋果的比較篩選:

DECLARE @MeterID INT = 1; 

WITH ValuesWithRowNumber 
AS 
(
    SELECT mv.MeterID 
      ,mv.RawValue 
      ,mv.[Timestamp] 
      ,ROW_NUMBER() OVER(PARTITION BY mv.MeterID ORDER BY mv.[Timestamp] ASC) RowNum 
    FROM dbo.MeterValues mv 
    WHERE mv.MeterID = @MeterID 
) 
SELECT crt.MeterID 
     ,crt.[Timestamp] AS CrtTimestamp 
     ,prev.[Timestamp] AS PrevTimestamp 
     ,crt.RawValue AS CrtRawValue 
     ,prev.RawValue AS PrevRawValue 
     ,(crt.RawValue - prev.RawValue)*1.00/DATEDIFF(SECOND, prev.[Timestamp], crt.[Timestamp]) Diff 
FROM ValuesWithRowNumber crt --crt=current 
JOIN ValuesWithRowNumber prev ON crt.RowNum - 1 = prev.RowNum 
ORDER BY crt.[Timestamp]; 
+0

'FORWARD_ONLY FAST_FORWARD READ_ONLY'? [FAST_FORWARD = FORWARD_ONLY + READ_ONLY](http://msdn.microsoft.com/en-us/library/ms180169.aspx) –

+0

我明白。我從一段明確的代碼複製/粘貼。 – nekno

+0

您是否使用IUN_MeterValues_MeterID_Timestamp索引進行測試? –

0
Try this 

Select a.metered,a.timestamp, 
    (a.rawValue-b.rawValue)/(a.timestamp-b.timestamp) 

From meters A 
Join (selec top 2 rawValue,Timestamp 
     From meters where metered = @meter 
     order by timestamp DESC) b 
On b.timestamp <> a.timestamp and a.meterId=B.meterId 

增加了DESC的時間戳中間查詢。這會導致返回最新的兩個時間戳,然後JOIN將「過濾」與當前行匹配的那個A

+0

您正在比較每個新值與相同的第一個值,然後計算自開始以來每個值的變化率。我相信@tbrew希望將每個值與前一個值進行比較,並且只計算自上一個值以來的變化率。另外,我認爲你加入第1行與第2行,然後加入第2行與第1行。後者會給你一個與前者相反的結果。不要誤會我的意思,我喜歡你的解決方案,我特別喜歡它是一個集合操作而不是一個遊標,我只是不確定它滿足要求。 – nekno

+0

Re:你的更新,現在你正在計算從值的時間點到最近的時間點的所有值的變化率。也就是說,沒有將每個值與以前的值進行比較。 – nekno

2

編輯1:我修改了索引定義以消除LookUp操作符=>邏輯讀取更少。

編輯2:我已經添加了第二個解決方案基於古怪更新方法。請閱讀由Jeff Moden撰寫的這篇文章(Solving the Running Total and Ordinal Rank Problems)。

第一解決方案可以與SQL Server 2005/2008進行測試:

--Create test table 
CREATE TABLE dbo.MeterValues 
(
    ID INT IDENTITY(1,1) PRIMARY KEY 
    ,[Timestamp] DATETIME NOT NULL 
    ,MeterID INT NOT NULL 
    ,RawValue INT NOT NULL 
); 

CREATE UNIQUE INDEX IUN_MeterValues_MeterID_Timestamp 
--SQL Server 2008 
ON dbo.MeterValues (MeterID, [Timestamp]) 
INCLUDE (RawValue) 
--SQL Server 2005 
--ON dbo.MeterValues (MeterID, [Timestamp],RawValue) 
--DROP INDEX dbo.MeterValues.IUN_MeterValues_MeterID_Timestamp 

--Insert some values 
INSERT dbo.MeterValues ([Timestamp], MeterID, RawValue) 
SELECT '2011-01-01T00:00:00', 1, 100 
UNION ALL 
SELECT '2011-01-01T00:00:15', 1, 105 
UNION ALL 
SELECT '2011-01-01T00:00:30', 1, 102 
UNION ALL 
SELECT '2011-01-01T00:00:45', 1, 108 
UNION ALL 
SELECT '2011-01-01T00:01:00', 1, 109 

UNION ALL 
SELECT '2011-01-01T00:00:00', 2, 1000 
UNION ALL 
SELECT '2011-01-01T00:00:15', 2, 900 
UNION ALL 
SELECT '2011-01-01T00:00:30', 2, 1105 
UNION ALL 
SELECT '2011-01-01T00:00:45', 2, 1050 
UNION ALL 
SELECT '2011-01-01T00:01:00', 2, 910; 

--Check test data 
SELECT * 
FROM dbo.MeterValues mv 
ORDER BY mv.MeterID, mv.ID DESC; 

--Solution 
WITH ValuesWithRowNumber 
AS 
(
    SELECT mv.MeterID 
      ,mv.RawValue 
      ,mv.[Timestamp] 
      ,ROW_NUMBER() OVER(PARTITION BY mv.MeterID ORDER BY mv.[Timestamp] ASC) RowNum 
    FROM dbo.MeterValues mv 
) 
SELECT crt.MeterID 
     ,crt.[Timestamp] AS CrtTimestamp 
     ,prev.[Timestamp] AS PrevTimestamp 
     ,crt.RawValue AS CrtRawValue 
     ,prev.RawValue AS PrevRawValue 
     ,(crt.RawValue - prev.RawValue)*1.00/DATEDIFF(SECOND, prev.[Timestamp], crt.[Timestamp]) Diff 
     ,STR((crt.RawValue - prev.RawValue)*1.00/DATEDIFF(SECOND, prev.[Timestamp], crt.[Timestamp])*100, 10, 2)+'%' [Percent] 
FROM ValuesWithRowNumber crt --crt=current 
LEFT JOIN ValuesWithRowNumber prev ON crt.MeterID = prev.MeterID --prev=previous 
AND  crt.RowNum - 1 = prev.RowNum 
ORDER BY crt.MeterID, crt.[Timestamp] DESC; 

--By, by 
DROP TABLE dbo.MeterValues; 

結果:

MeterID  CrtTimestamp   PrevTimestamp   CrtRawValue PrevRawValue Diff         Percent 
----------- ----------------------- ----------------------- ----------- ------------ --------------------------------------- ----------- 
1   2011-01-01 00:01:00.000 2011-01-01 00:00:45.000 109   108   0.0666666666666        6.67% 
1   2011-01-01 00:00:45.000 2011-01-01 00:00:30.000 108   102   0.4000000000000        40.00% 
1   2011-01-01 00:00:30.000 2011-01-01 00:00:15.000 102   105   -0.2000000000000       -20.00% 
1   2011-01-01 00:00:15.000 2011-01-01 00:00:00.000 105   100   0.3333333333333        33.33% 
1   2011-01-01 00:00:00.000 NULL     100   NULL   NULL         NULL 
2   2011-01-01 00:01:00.000 2011-01-01 00:00:45.000 910   1050   -9.3333333333333       -933.33% 
2   2011-01-01 00:00:45.000 2011-01-01 00:00:30.000 1050  1105   -3.6666666666666       -366.67% 
2   2011-01-01 00:00:30.000 2011-01-01 00:00:15.000 1105  900   13.6666666666666       1366.67% 
2   2011-01-01 00:00:15.000 2011-01-01 00:00:00.000 900   1000   -6.6666666666666       -666.67% 
2   2011-01-01 00:00:00.000 NULL     1000  NULL   NULL         NULL 

第二個解決方案可以/應與SQL 2000/2005/2008工作(請參閱「規則從傑夫MODEN文章」部分):

--Create test table 
CREATE TABLE dbo.MeterValues 
(
    MeterID INT NOT NULL 
    ,[Timestamp] DATETIME NOT NULL  
    ,RawValue INT NOT NULL 
    ,Diff NUMERIC(10,3) NULL 
    ,PRIMARY KEY CLUSTERED(MeterID,[Timestamp]) 
); 

--Insert some values 
INSERT dbo.MeterValues ([Timestamp], MeterID, RawValue) 
SELECT '2011-01-01T00:00:00', 1, 100 
UNION ALL 
SELECT '2011-01-01T00:00:15', 1, 105 
UNION ALL 
SELECT '2011-01-01T00:00:30', 1, 102 
UNION ALL 
SELECT '2011-01-01T00:00:45', 1, 108 
UNION ALL 
SELECT '2011-01-01T00:01:00', 1, 109 

UNION ALL 
SELECT '2011-01-01T00:00:00', 2, 1000 
UNION ALL 
SELECT '2011-01-01T00:00:15', 2, 900 
UNION ALL 
SELECT '2011-01-01T00:00:30', 2, 1105 
UNION ALL 
SELECT '2011-01-01T00:00:45', 2, 1050 
UNION ALL 
SELECT '2011-01-01T00:01:00', 2, 910; 

--Check test data 
SELECT * 
FROM dbo.MeterValues mv 
ORDER BY mv.MeterID, mv.[Timestamp]; 

DECLARE @OldRawValue INT 
     ,@Diff NUMERIC(10,3) 
     ,@OldMeterID INT 
     ,@OldTimestamp DATETIME; 

PRINT '*****Star*****'    
--Calculations 
UPDATE dbo.MeterValues WITH(TABLOCKX) 
SET  @Diff = CASE WHEN @OldMeterID = MeterID THEN (RawValue - @OldRawValue)*1.00/DATEDIFF(SECOND,@OldTimeStamp,[TimeStamp]) END 
     ,Diff = @Diff 
     ,@OldRawValue = RawValue 
     ,@OldMeterID = MeterID 
     ,@OldTimestamp = [Timestamp]   
OPTION(MAXDOP 1); 

--Results 
SELECT * 
FROM dbo.MeterValues mv 
ORDER BY mv.MeterID, mv.[Timestamp]; 
PRINT '*****Stop*****' 

--By, by 
DROP TABLE dbo.MeterValues; 

結果:

MeterID  Timestamp    RawValue Diff 
----------- ----------------------- ----------- --------------------------------------- 
1   2011-01-01 00:01:00.000 109   0.067 
1   2011-01-01 00:00:45.000 108   0.400 
1   2011-01-01 00:00:30.000 102   -0.200 
1   2011-01-01 00:00:15.000 105   0.333 
1   2011-01-01 00:00:00.000 100   NULL 
2   2011-01-01 00:01:00.000 910   -9.333 
2   2011-01-01 00:00:45.000 1050  -3.667 
2   2011-01-01 00:00:30.000 1105  13.667 
2   2011-01-01 00:00:15.000 900   -6.667 
2   2011-01-01 00:00:00.000 1000  NULL