0

您好我在 SQL Server 数据库中有一个仪表值表,其中包含一个包含以下列的表:

Timestamp, meterID, rawValue

我正在尝试使用查询和谷歌图表来绘制用水率,问题是我需要根据每 15 到 30 分钟更新一次的原始仪表值来计算用水率。

我想运行一个返回特定水表值的查询。

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

任何帮助深表感谢。

4

3 回答 3

2

编辑 1:我修改了索引定义以消除查找运算符 => 更少的逻辑读取。

编辑 2:我添加了基于古怪更新方法的第二个解决方案。请阅读Jeff Moden 撰写的这篇文章(解决运行总和序数问题)。

第一个解决方案可以使用 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(请阅读 Jeff 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
于 2011-09-20T08:05:37.697 回答
0

我对我的查询和@Bogdan 的查询进行了一些小的更改,以使它们尽可能相似,然后进行比较。Bogdan 修改后的查询在这篇文章的底部。

在同一个查询中堆叠在一起,根据 SQL Server 查询执行计划,我的是 53% 的查询成本,而 Bogdan 的是 47%。

对于 Bogdan 帖子中提供的数据集:

  • 我的查询:6 次扫描和 27 次逻辑读取
  • Bogdan 的:6 次扫描和 72 次逻辑读取

我每 15 秒为meterID 1 和 2 添加值最多 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

Bogdan 修改后的查询,按 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];
于 2011-09-20T04:47:27.883 回答
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 中的当前行匹配的那个

于 2011-09-20T04:50:32.597 回答