Select latest and 2nd latest date rows per user

  • I have the following query to select rows where the LAST_UPDATE_DATE field is getting records that have a date value greater than or equal to the last 7 days, which works great.

     SELECT 'NEW ROW' AS 'ROW_TYPE', A.EMPLID, B.FIRST_NAME, B.LAST_NAME,
      A.BANK_CD, A.ACCOUNT_NUM, ACCOUNT_TYPE, PRIORITY, A.LAST_UPDATE_DATE 

     FROM PS_DIRECT_DEPOSIT D 
    INNER JOIN PS_DIR_DEP_DISTRIB A ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT 
    INNER JOIN PS_EMPLOYEES B ON B.EMPLID = A.EMPLID 

    WHERE   B.EMPL_STATUS NOT IN ('T','R','D') 
    AND ((A.DEPOSIT_TYPE = 'P' AND A.AMOUNT_PCT = 100)
           OR A.PRIORITY = 999   
           OR A.DEPOSIT_TYPE = 'B') 
    AND A.EFFDT = (SELECT MAX(A1.EFFDT)  
                   FROM PS_DIR_DEP_DISTRIB A1   
                   WHERE A1.EMPLID = A.EMPLID   
                    AND A1.EFFDT <= GETDATE()) 
    AND D.EFF_STATUS = 'A' 
    AND D.EFFDT = (SELECT MAX(D1.EFFDT)  
                   FROM PS_DIRECT_DEPOSIT D1   
                   WHERE D1.EMPLID = D.EMPLID    
                   AND D1.EFFDT <= GETDATE()) 
    AND A.LAST_UPDATE_DATE >= GETDATE() - 7

    What I would like to add onto this is to also add the previous (2nd MAX) row per EMPLID, so that I can output the 'old' row (that was prior to the last update the latest row meeting above criteria),
    along with the new row that I already am outputting in the query. The result would look something similar to this:

    ROW_TYPE  EMPLID  FIRST_NAME LAST_NAME  BANK_CD    ACCOUNT_NUM   ACCOUNT_TYPE  PRIORITY  LAST_UPDATE_DATE
    NEW
    ROW   12345  JOHN    SMITH    123548999  45234879   C      999    2019-03-06 00:00:00.000
    OLD
    ROW   12345  JOHN    SMITH    214080046  92178616   C      999    2018-10-24 00:00:00.000
    NEW
    ROW   56399  CHARLES   MASTER    785816167  84314314   C      999    2019-03-07 00:00:00.000 
    OLD
    ROW   56399  CHARLES   MASTER    345761227  547352    C      999    2017-05-16 00:00:00.000

    So the EMPLID would be ordered by NEW ROW, followed by OLD ROW as shown above. In this example the 'NEW ROW' is getting the record that is within the past 7 days, as indicated by the [/code] would be ordered by NEW ROW, followed by OLD ROW as shown above. In this example the 'NEW ROW' is getting the record that is within the past 7 days, as indicated by the LAST_UPDATE_DATE.

    I would like to get feedback on how to modify the query so I can also get the 'old' row (which is the max row that is less than the 'NEW' row retrieved above).

  • Use a CTE/ROW_NUMBER().  Since you didn't provide consumable data, this isn't tested.

    WITH CTE AS
    (
         SELECT
            A.EMPLID
        ,    B.FIRST_NAME
        ,    B.LAST_NAME
        ,    A.BANK_CD
        ,    A.ACCOUNT_NUM
        ,    ACCOUNT_TYPE
        ,    [PRIORITY]
        ,    A.LAST_UPDATE_DATE
        ,    ROW_NUMBER() OVER(PARTITION BY A.EMPLID ORDER BY A.EFFDT, D.EFFDT) AS rn
        ,    MAX(A.LAST_UPDATE_DATE) OVER(PARTITION BY A.EMPLID) AS MAX_UPDATE_DATE
        FROM PS_DIRECT_DEPOSIT D 
        INNER JOIN PS_DIR_DEP_DISTRIB A
            ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT 
        INNER JOIN PS_EMPLOYEES B
            ON B.EMPLID = A.EMPLID
        WHERE   B.EMPL_STATUS NOT IN ('T','R','D') 
        AND ((A.DEPOSIT_TYPE = 'P' AND A.AMOUNT_PCT = 100)
               OR A.PRIORITY = 999   
               OR A.DEPOSIT_TYPE = 'B') 
        AND D.EFF_STATUS = 'A'
    )

    SELECT *
    FROM CTE
    WHERE rn = 2
    AND MAX_UPDATE_DATE >= GETDATE() - 7

    Drew

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • Hi Drew, this seems to work great! I made a slight tweak to the final select to output rows 1 and 2 (the 'new' and 'old' rows). Question, is there a way to have a column named ROW_TYPE and output the value 'NEW VALUE' for rn = 1 and output 'OLD VALUE' for rn = 2 ? I wasn't sure if this is possible to do in a CTE...

    Also just looking at the results of the inner query within the CTE, here is a sample of the results I am getting:

    EMPLID    FIRST_NAME    LAST_NAME    BANK_CD               ACCOUNT_NUM    ACCOUNT_TYPE    PRIORITY    LAST_UPDATE_DATE          rn      MAX_UPDATE_DATE
    1234     JOHN           SMITH        021301115             132077051      C               310         2014-03-05 00:00:00.000   1      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077035      C               310         2014-03-05 00:00:00.000   2      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077682      C               999         2015-11-26 00:00:00.000   3      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        022701115             132074784      C               999         2019-02-13 00:00:00.000   4      2019-02-13 00:00:00.000

    As you can see it is outputting rn (row number) 1 with the oldest LAST_UPDATE_DATE row, whereas I would expect the MAX LAST_UPDATE_DATE for this ID to be rn 1. Can it easily be reordered?

    Thanks again,
    Kevin

  • kdrymer - Friday, March 8, 2019 4:58 PM

    Hi Drew, this seems to work great! I made a slight tweak to the final select to output rows 1 and 2 (the 'new' and 'old' rows). Question, is there a way to have a column named ROW_TYPE and output the value 'NEW VALUE' for rn = 1 and output 'OLD VALUE' for rn = 2 ? I wasn't sure if this is possible to do in a CTE...

    Also just looking at the results of the inner query within the CTE, here is a sample of the results I am getting:

    EMPLID    FIRST_NAME    LAST_NAME    BANK_CD               ACCOUNT_NUM    ACCOUNT_TYPE    PRIORITY    LAST_UPDATE_DATE          rn      MAX_UPDATE_DATE
    1234     JOHN           SMITH        021301115             132077051      C               310         2014-03-05 00:00:00.000   1      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077035      C               310         2014-03-05 00:00:00.000   2      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077682      C               999         2015-11-26 00:00:00.000   3      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        022701115             132074784      C               999         2019-02-13 00:00:00.000   4      2019-02-13 00:00:00.000

    As you can see it is outputting rn (row number) 1 with the oldest LAST_UPDATE_DATE row, whereas I would expect the MAX LAST_UPDATE_DATE for this ID to be rn 1. Can it easily be reordered?

    Thanks again,
    Kevin

    If my understanding is correct . I think you need to include 
    rn  in (1,2) in the below query
    WITH CTE AS
    (
      SELECT
       A.EMPLID
      ,  B.FIRST_NAME
      ,  B.LAST_NAME
      ,  A.BANK_CD
      ,  A.ACCOUNT_NUM
      ,  ACCOUNT_TYPE
      ,  [PRIORITY]
      ,  A.LAST_UPDATE_DATE
      ,  ROW_NUMBER() OVER(PARTITION BY A.EMPLID ORDER BY A.EFFDT, D.EFFDT) AS rn
      ,  MAX(A.LAST_UPDATE_DATE) OVER(PARTITION BY A.EMPLID) AS MAX_UPDATE_DATE
      FROM PS_DIRECT_DEPOSIT D
      INNER JOIN PS_DIR_DEP_DISTRIB A
       ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT
      INNER JOIN PS_EMPLOYEES B
       ON B.EMPLID = A.EMPLID
      WHERE B.EMPL_STATUS NOT IN ('T','R','D')
      AND ((A.DEPOSIT_TYPE = 'P' AND A.AMOUNT_PCT = 100)
        OR A.PRIORITY = 999 
        OR A.DEPOSIT_TYPE = 'B')
      AND D.EFF_STATUS = 'A'
    )

    SELECT *
    FROM CTE
    WHERE rn  in (1,2)
    AND MAX_UPDATE_DATE >= GETDATE() - 7

    Saravanan

  • saravanatn - Friday, March 8, 2019 10:45 PM

    kdrymer - Friday, March 8, 2019 4:58 PM

    Hi Drew, this seems to work great! I made a slight tweak to the final select to output rows 1 and 2 (the 'new' and 'old' rows). Question, is there a way to have a column named ROW_TYPE and output the value 'NEW VALUE' for rn = 1 and output 'OLD VALUE' for rn = 2 ? I wasn't sure if this is possible to do in a CTE...

    Also just looking at the results of the inner query within the CTE, here is a sample of the results I am getting:

    EMPLID    FIRST_NAME    LAST_NAME    BANK_CD               ACCOUNT_NUM    ACCOUNT_TYPE    PRIORITY    LAST_UPDATE_DATE          rn      MAX_UPDATE_DATE
    1234     JOHN           SMITH        021301115             132077051      C               310         2014-03-05 00:00:00.000   1      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077035      C               310         2014-03-05 00:00:00.000   2      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077682      C               999         2015-11-26 00:00:00.000   3      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        022701115             132074784      C               999         2019-02-13 00:00:00.000   4      2019-02-13 00:00:00.000

    As you can see it is outputting rn (row number) 1 with the oldest LAST_UPDATE_DATE row, whereas I would expect the MAX LAST_UPDATE_DATE for this ID to be rn 1. Can it easily be reordered?

    Thanks again,
    Kevin

    If my understanding is correct . I think you need to include 
    rn  in (1,2) in the below query
    WITH CTE AS
    (
      SELECT
       A.EMPLID
      ,  B.FIRST_NAME
      ,  B.LAST_NAME
      ,  A.BANK_CD
      ,  A.ACCOUNT_NUM
      ,  ACCOUNT_TYPE
      ,  [PRIORITY]
      ,  A.LAST_UPDATE_DATE
      ,  ROW_NUMBER() OVER(PARTITION BY A.EMPLID ORDER BY A.EFFDT, D.EFFDT) AS rn
      ,  MAX(A.LAST_UPDATE_DATE) OVER(PARTITION BY A.EMPLID) AS MAX_UPDATE_DATE
      FROM PS_DIRECT_DEPOSIT D
      INNER JOIN PS_DIR_DEP_DISTRIB A
       ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT
      INNER JOIN PS_EMPLOYEES B
       ON B.EMPLID = A.EMPLID
      WHERE B.EMPL_STATUS NOT IN ('T','R','D')
      AND ((A.DEPOSIT_TYPE = 'P' AND A.AMOUNT_PCT = 100)
        OR A.PRIORITY = 999 
        OR A.DEPOSIT_TYPE = 'B')
      AND D.EFF_STATUS = 'A'
    )

    SELECT *
    FROM CTE
    WHERE rn  in (1,2)
    AND MAX_UPDATE_DATE >= GETDATE() - 7

    I actually meant to use rn <= 2 rather than rn=2.  I think it's simpler update if the number of rows required changes.

    Drew

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • kdrymer - Friday, March 8, 2019 4:58 PM

    Hi Drew, this seems to work great! I made a slight tweak to the final select to output rows 1 and 2 (the 'new' and 'old' rows). Question, is there a way to have a column named ROW_TYPE and output the value 'NEW VALUE' for rn = 1 and output 'OLD VALUE' for rn = 2 ? I wasn't sure if this is possible to do in a CTE...

    Also just looking at the results of the inner query within the CTE, here is a sample of the results I am getting:

    EMPLID    FIRST_NAME    LAST_NAME    BANK_CD               ACCOUNT_NUM    ACCOUNT_TYPE    PRIORITY    LAST_UPDATE_DATE          rn      MAX_UPDATE_DATE
    1234     JOHN           SMITH        021301115             132077051      C               310         2014-03-05 00:00:00.000   1      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077035      C               310         2014-03-05 00:00:00.000   2      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        021301115             132077682      C               999         2015-11-26 00:00:00.000   3      2019-02-13 00:00:00.000
    1234     JOHN           SMITH        022701115             132074784      C               999         2019-02-13 00:00:00.000   4      2019-02-13 00:00:00.000

    As you can see it is outputting rn (row number) 1 with the oldest LAST_UPDATE_DATE row, whereas I would expect the MAX LAST_UPDATE_DATE for this ID to be rn 1. Can it easily be reordered?

    Thanks again,
    Kevin

    This is why we ask for sample data and expected results.  It makes it much harder to test without it.  Just change the ORDER BY in the OVER clause to DESC instead of the default ASC where appropriate.

    You can output the NEW/OLD VALUE in the CTE, but it's much simpler to do it in the main query based on the rn field.  It's a simple CASE expression.

    Drew

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • You can try the below query.   It wasn't tested precisely because you provided no sample data to work with:
    WITH CTE AS (

        SELECT
            A.EMPLID
        ,    B.FIRST_NAME
        ,    B.LAST_NAME
        ,    A.BANK_CD
        ,    A.ACCOUNT_NUM
        ,    ACCOUNT_TYPE
        ,    [PRIORITY]
        ,    A.LAST_UPDATE_DATE
        ,    ROW_NUMBER() OVER(PARTITION BY A.EMPLID ORDER BY A.EFFDT DESC, D.EFFDT DESC) AS rn
        ,    MAX(A.LAST_UPDATE_DATE) OVER(PARTITION BY A.EMPLID) AS MAX_UPDATE_DATE
        FROM PS_DIRECT_DEPOSIT D
        INNER JOIN PS_DIR_DEP_DISTRIB A
            ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT
        INNER JOIN PS_EMPLOYEES B
            ON B.EMPLID = A.EMPLID
        WHERE    B.EMPL_STATUS NOT IN ('T','R','D')
            AND D.EFF_STATUS = 'A'
            AND (
                    (
                    A.DEPOSIT_TYPE = 'P'
                    AND
                    A.AMOUNT_PCT = 100
                    )
                    OR
                    A.PRIORITY = 999
                    OR
                    A.DEPOSIT_TYPE = 'B'
                    )
    )
    SELECT
        CASE rn
            WHEN 1 THEN 'NEW ROW'
            WHEN 2 THEN 'OLD ROW'
        END AS ROW_TYPE
        ,    *
    FROM CTE
    WHERE    rn <= 2
        AND MAX_UPDATE_DATE >= GETDATE() - 7
    ORDER BY EMPLID, rn;

    Steve (aka sgmunson) 🙂 🙂 🙂
    Rent Servers for Income (picks and shovels strategy)

  • Thanks all for the help!

Viewing 8 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic. Login to reply