2011-09-20 99 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