简体   繁体   中英

Getting the value of a previous record using ROW_NUMBER() in SQL Server

Hopefully this is easy enough for those more experienced in SQL Server.

I have a table to customer loan activity data which is updated whenever an action happens on their account. For example if their limit is increased, a new record will be created with their new limit. I want to be able to create a listing of their activity where the activity amount is their new limit subtracting whatever their previous limit was.

At the moment I have the following but I'm struggling to work out how to access that previous record.

SELECT   
    CUSTOMER
    ,LEDGER
    ,ACCOUNT
    ,H.AMOUNT - COALESCE(X.AMOUNT, 0)
FROM 
    dbo.ACTIVITY H WITH (NOLOCK)
LEFT OUTER JOIN 
    (SELECT  
        CUSTOMER
        ,LEDGER
        ,ACCOUNT
        ,ACTIVITY_DATE
        ,AMOUNT
        ,ROW_NUMBER() OVER (PARTITION BY CUSTOMER, LEDGER, ACCOUNT ORDER BY ACTIVITY_DATE ASC) AS ROW_NUMBER
     FROM 
         dbo.ACTIVITY WITH (NOLOCK)) X ON H.CUSTOMER = X.CUSTOMER
                                          AND H.LEDGER = X.LEDGER
                                          AND H.ACCOUNT = X.ACCOUNT

So basically I only want to subtract x.amount if it's the previous record but I'm not sure how to do this when I don't know what day it happened.

I thought Row_Number() would help me but I'm still a bit stumped.

Hope you hear from you all soon :)

Cheers

Here's a query that will only pass through dbo.Activity ONCE

    SELECT H.CUSTOMER
          ,H.LEDGER
          ,H.ACCOUNT
          ,MAX(H.ACTIVITY_DATE) ACTIVITY_DATE
          ,SUM(CASE X.I WHEN 1 THEN AMOUNT ELSE -AMOUNT END) AMOUNT
      FROM (SELECT CUSTOMER
                  ,LEDGER
                  ,ACCOUNT
                  ,ACTIVITY_DATE
                  ,AMOUNT
                  ,ROW_NUMBER() OVER (PARTITION BY CUSTOMER, LEDGER, ACCOUNT ORDER BY ACTIVITY_DATE DESC) AS ROW_NUMBER
             FROM dbo.ACTIVITY WITH (NOLOCK)
           ) H
CROSS JOIN (select 1 union all select 2) X(I)
     WHERE ROW_NUMBER - X.I >= 0
  GROUP BY H.CUSTOMER
          ,H.LEDGER
          ,H.ACCOUNT
          ,ROW_NUMBER - X.I;

And here's the DDL/DML for some data I used to test

CREATE TABLE dbo.ACTIVITY(CUSTOMER int, LEDGER int, ACCOUNT int, ACTIVITY_DATE datetime, AMOUNT int)
INSERT dbo.ACTIVITY select
    1,2,3,GETDATE(),123 union all select
    1,2,3,GETDATE()-1,16 union all select
    1,2,3,GETDATE()-2,12 union all select
    1,2,3,GETDATE()-3,1 union all select
    4,5,6,GETDATE(),1000 union all select
    4,5,6,GETDATE()-6,123 union all select
    7,7,7,GETDATE(),99;

Alternatives

A more traditional approach using a subquery to get the previous row:

  SELECT CUSTOMER, LEDGER, ACCOUNT, ACTIVITY_DATE,
         AMOUNT - ISNULL((SELECT TOP(1) I.AMOUNT
                            FROM dbo.ACTIVITY I
                           WHERE I.CUSTOMER = O.CUSTOMER
                             AND I.LEDGER = O.LEDGER
                             AND I.ACCOUNT = O.ACCOUNT
                             AND I.ACTIVITY_DATE < O.ACTIVITY_DATE
                        ORDER BY I.ACTIVITY_DATE DESC), 0) AMOUNT
    FROM dbo.ACTIVITY O
ORDER BY CUSTOMER, LEDGER, ACCOUNT, ACTIVITY_DATE;

Or ROW_NUMBER() the data twice and join between them

   SELECT A.CUSTOMER, A.LEDGER, A.ACCOUNT, A.ACTIVITY_DATE, 
          A.AMOUNT - ISNULL(B.AMOUNT,0) AMOUNT
     FROM (SELECT *, RN=ROW_NUMBER() OVER (partition by CUSTOMER, LEDGER, ACCOUNT
                                          order by ACTIVITY_DATE ASC)
            FROM dbo.ACTIVITY) A
LEFT JOIN (SELECT *, RN=ROW_NUMBER() OVER (partition by CUSTOMER, LEDGER, ACCOUNT
                                          order by ACTIVITY_DATE ASC)
            FROM dbo.ACTIVITY) B ON A.CUSTOMER = B.CUSTOMER
                              AND A.LEDGER = B.LEDGER
                              AND A.ACCOUNT = B.ACCOUNT
                              AND B.RN = A.RN-1 -- prior record
ORDER BY A.CUSTOMER, A.LEDGER, A.ACCOUNT, A.ACTIVITY_DATE;

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