简体   繁体   中英

Subtract previous row value to current row and order by date

Data as follows in my table

SELECT ID, VALUE, acc_no, adate 
FROM TB_DailyStatement

   id    value   acc_no       adate
    ---------------------------------------------
    1     12     1      2019-01-01 07:40:38.250
    2     14     1      2019-01-02 07:41:05.883
    3     15     1      2019-01-13 07:41:22.377
    4     10     2      2019-01-14 08:15:53.403
    5     16     2      2019-01-03 13:52:47.347 
    6     19     1      2019-01-09 13:53:56.317
    7      7     3      2019-01-17 00:00:00.000
    8     24     2      2019-01-17 00:00:00.000
    9     19     2      2019-01-02 00:00:00.000
    10     7     1      2019-01-07 00:00:00.000
    11    24     1      2019-01-05 14:12:47.080
    12    20     3      2019-01-28 00:00:00.000

Expected result

    id     value   acc_no       aDATE                   result 
    ------------------------------------------------------------------------
    1     12     1        2019-01-01 07:40:38.250        12 (current row values of acc_no=1) 
    2     14     1        2019-01-02 07:41:05.883         2 (14 (current row values)-12(previous row value of acc_no=1))  
   11     24     1        2019-01-05 14:12:47.080        10 (24 (current row values)-14(previous date  value of acc_no=1))  
   10      7     1        2019-01-07 00:00:00.000       -17 (7 (current row values)-24(previous date  value of acc_no=1))  
    6     19     1        2019-01-09 13:53:56.317        12 (19 (current row values)-7(previous date  value of acc_no=1))  
    3     15     1        2019-01-13 07:41:22.377        -4 (15 (current row values)-19(previous date  value of acc_no=1))  
    9     19     2        2019-01-02 00:00:00.000        19 (12 (current row values of acc_no=2) 
    5     16     2        2019-01-03 13:52:47.347        -3 (16 (current row values)-14(previous date  value of acc_no=2))  
    4     10     2        2019-01-14 08:15:53.403        -6 (10 (current row values)-16(previous date  value of acc_no=2))  
    8     24     2        2019-01-17 00:00:00.000        14 (24 (current row values)-10(previous date  value of acc_no=2))  
    7      7     3        2019-01-17 00:00:00.000         7 (12(current row values of acc_no=3) 
   12     20     3        2019-01-28 00:00:00.000        13 (20 (current row values)-7(previous date  value of acc_no=3)) 

I have tried the following query

SELECT 
    id, t.value, acc_no, adate, 
    t.value - ISNULL(v.value, 0) AS result 
FROM 
    TB_DailyStatementt
OUTER APPLY 
    (SELECT TOP (1) value
     FROM TB_DailyStatement
     WHERE id < t.id
       AND acc_no = t.acc_no
     ORDER by id DESC) v

This returns some output, but I am not able to use order by clause ie adate and acc_no

If you want to get this result just simple order by acc_no asc you can get simple your expected result,

For example

Select * from table order by acc_no acs

In SQL Server 2008 , you can't use LEAD & LAG , for this you can use query like following using OUTER APPLY .

select * 
from  TB_DailyStatementt tout 
       outer apply (select top 1 value Prev 
                    from   TB_DailyStatementt t1 
                    where  t1.acc_no = tout.acc_no 
                           AND t1.adate < tout.adate 
                    order  by t1.adate desc)t1 
       outer apply (select top 1 value Next 
                    from   TB_DailyStatementt  t1 
                    where  t1.acc_no = tout.acc_no 
                           AND t1.adate > tout.adate 
                    order by t1.adate asc)t2 
       order by tout.adate,acc_no

This will give you the required previous and next values depending on acc_no. Now you can apply switch case to format the string.

Check this Demo

Declare @ds TABLE (id int, value int, acc_no int, dt datetime)
INSERT INTO @ds
SELECT  1, 12, 1, '2019-01-01 07:40:38.250' UNION
SELECT  2, 14, 1, '2019-01-02 07:41:05.883' UNION
SELECT  3, 15, 1, '2019-01-13 07:41:22.377' UNION
SELECT  4, 10, 2, '2019-01-14 08:15:53.403' UNION
SELECT  5, 16, 2, '2019-01-03 13:52:47.347' UNION
SELECT  6, 19, 1, '2019-01-09 13:53:56.317' UNION
SELECT  7,  7, 3, '2019-01-17 00:00:00.000' UNION
SELECT  8, 24, 2, '2019-01-17 00:00:00.000' UNION
SELECT  9, 19, 2, '2019-01-02 00:00:00.000' UNION
SELECT 10,  7, 1, '2019-01-07 00:00:00.000' UNION
SELECT 11, 24, 1, '2019-01-05 14:12:47.080' UNION
SELECT 12, 20, 3, '2019-01-28 00:00:00.000'

SELECT id, value, acc_no, dt, value - previous AS result
FROM (
  SELECT ROW_NUMBER() OVER (PARTITION BY DS1.id
                            ORDER BY     DS2.dt DESC) AS rn
  ,DS1.*, COALESCE(DS2.value,0) AS previous
  FROM      @ds DS1
  LEFT JOIN @ds DS2
    ON      DS2.acc_no = DS1.acc_no
    AND     DS2.dt     < DS1.dt
) AS dt
WHERE rn = 1

In your original query, you are joining on ID, the your comments in the desired results show that you want to use the date for ordering, not the id.

So you could fix your original query by changing "id < t.id" with "adate < t.adate"

I think you are on the right track. You need an order by in the outer query and to fix the table aliases:

SELECT ds.id, ds.value, ds.acc_no, ds.adate, 
       (t.value - COALESCE(prev.value, 0)) AS result 
FROM TB_DailyStatementt ds OUTER APPLY 
    (SELECT TOP (1) ds2.value
     FROM TB_DailyStatement ds2
     WHERE ds2.id < ds.t.id AND ds2.acc_no = ds.acc_no
     ORDER by ds2.id DESC
    ) prev
ORDER BY ds.acc_no, ds.adate

Try this:

    Select  *, CASE WHEN LAG(value) OVER(partition by acc_no order by adate) IS NULL THEN value
    ELSE value - LAG(value) OVER(partition by acc_no order by adate) END  result from TB_DailyStatement

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