简体   繁体   中英

Subtracting Previous Value by Prior Group - SQL Server

This question has been asked previously and been well answered for MySQL specifically, however I'd very much appreciate your guys' help in translating this for SQL Server. The prior posts are closed and I didn't want to reopen them as they're quite old.

Below is the dataset to query, and the required output of the query solution. Below that is the excellent MySql solution posted here ( SQL best way to subtract a value of the previous row in the query? ). My question is how to accomplish the same result with variables in SQL Server. I apologize if this is a novice question, but I'm new to variables in SQL Server and have failed in my attempts to translate to SQL Server based on MSDN documentation on variables in SQL Server.

Thank you for your help!

select
      EL.SN,
      EL.Date,
      EL.EL.Value,
      if( @lastSN = EL.SN, EL.Value - @lastValue, 0000.00 ) as Consumption,
      @lastSN := EL.SN,
      @lastValue := EL.Value
   from
      EnergyLog EL,
      ( select @lastSN := 0,
               @lastValue := 0 ) SQLVars
   order by
      EL.SN,
      EL.Date

I need to have the consumption value base on previous one by SN number. This is my data:

TABLE EnergyLog

SN     Date                 Value
2380   2012-10-30 00:15:51  21.01
2380   2012-10-31 00:31:03  22.04
2380   2012-11-01 00:16:02  22.65
2380   2012-11-02 00:15:32  23.11
20100  2012-10-30 00:15:38  35.21
20100  2012-10-31 00:15:48  37.07
20100  2012-11-01 00:15:49  38.17
20100  2012-11-02 00:15:19  38.97
20103  2012-10-30 10:27:34  57.98
20103  2012-10-31 12:24:42  60.83

This is the result I need:

SN      Date                 Value  consumption
2380    2012-10-30 00:15:51  21.01  0
2380    2012-10-31 00:31:03  22.04  1.03
2380    2012-11-01 00:16:02  22.65  0.61
2380    2012-11-02 00:15:32  23.11  0.46
20100   2012-10-30 00:15:38  35.21  0
20100   2012-10-31 00:15:48  37.07  1.86
20100   2012-11-01 00:15:49  38.17  1.1
20100   2012-11-02 00:15:19  38.97  0.8
20103   2012-10-30 10:27:34  57.98  0
20103   2012-10-31 12:24:42  60.83  2.85

By using a CTE you would be able to get the data ordered, and then select the previous record.

WITH PREVCTE AS (
SELECT  *, 
        ROW_NUMBER() OVER (PARTITION BY SN ORDER BY MyDate) [RowNum] 
FROM @myTable
)
SELECT A.SN, 
       A.MyDate, 
       A.[Value], 
       A.[Value] - COALESCE(B.[Value], A.[Value]) [Consumption]
FROM PREVCTE A LEFT OUTER JOIN PREVCTE B 
    ON   A.RowNum - 1 = B.RowNum AND  
         A.SN = B.SN 

SQL Fiddle Demo

My previous solution had calculated running totals. After shawnt00's comment and looking back at the example that you've provided I see that's not the case. This should make a solution much easier. I believe that the following will work for SQL 2008, although I don't have a 2008 box handy at the moment to test it.

;WITH CTE_Ordered AS
(
    SELECT
        sn,
        [date],
        value,
        ROW_NUMBER() OVER(PARTITION BY sn ORDER BY [date]) AS row_num
    FROM
        dbo.EnergyLog
)
SELECT
    T1.sn,
    T1.[date],
    T1.value,
    T1.value - COALESCE(T2.value, 0) AS consumption
FROM
    CTE_Ordered T1
LEFT OUTER JOIN CTE_Ordered T2 ON
    T2.sn = T1.sn AND
    T2.row_num = T1.row_num - 1
ORDER BY
    T1.sn,
    T1.row_num

I believe you're looking for the difference between adjacent dates. Here's one way.

select
    SN, Date, Value,
    Value - (
        select coalesce(max(Value), 0) from EnergyLog el2
        where el2.SN = el.SN and el2.Date < el.Date
        group by el2.SN
    ) as consumption
from EnergyLog el

You might get a performance boost by narrowing the size of the look-back window if you can always assume the time between is capped. That's going to depending on indexing too.

select
    SN, Date, Value,
    Value - (
        select coalesce(max(Value), 0) from EnergyLog el2
        where el2.SN = el.SN
            and el2.Date > dateadd(hour, -48, el.Date) /* max gap 48 hrs? */
            and el2.Date < el.Date
        group by el2.SN
    ) as consumption
from EnergyLog el

EDIT: Based on your comment below regarding sequence numbers:

select
    SN, Date, Value,
    Value - (
        select coalesce(max(Value), 0) from EnergyLog el2
        where el2.SN = el.SN and el2.SeqNo = el.SeqNo - 1
        group by el2.SN
    ) as consumption
from EnergyLog el

You can certainly use an outer join rather than the scalar subquery. Of course if you already have a sequence number then you won't need row_number() to generate them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM