Search code examples
sqlsql-server-2012

History data update


We have requirement to check the Auto Parking Membership activity. When an individual becomes member the details will stored initially with MemberID, MemberShipDate, ActivityDate, ActivityMonthEnd, IsActive, ActiveYTD. This data will be stored into DW table. This is daily data which we are storing to DW. The last field ‘ActiveYTD’ has little complex logic. The default value is ‘Y’, This field value depends on the ‘IsActive’ field. When IsActive = ‘N’ The ‘ActiveYTD’ field value also updated as ‘N’ from the day the IsActive field value changed from Y to N.
But there is a business rule this IsActive field can be Active and Inactive any number of times. I don’t know why that rule is like that.

Note:- If suppose the IsActive field value = ‘N’ on Feb 15 2022 and is inactive until April 20 2022. Certainly the ActivityYTD field will also change from Y to N based on above formula logic. But since the IsActive field changed to ‘Y’ on April 21 2022. The ActivityYTD will also change to ‘Y’. But the business definition is when the IsActive field changes to Y from N. The ActiveYTD field should never be ‘N’ in previous rows. It should be ‘Y’ for all rows from Feb 15 2022 to till date. So in a scenario if the IsActive value = ‘N’ from Feb 15 2022 till end of the year the ActiveYTD will also be ‘N’ starting Feb 15th 2022. Both ActivityDate and ActivityMonthEndDate are keys from a dimension table. (eg: - 20220215, 20220228) Based on the above business logic defined, I have created a query

Select mainData.MemberID, mainData.MembershipDate, mainData.ActivityDateKey, mainData.ActivityMonthEndDateKey,  InactiveData.InactiveDate, mainData.IsActive, 
Case When mainData.IsAcitve =’N’ and (mainData.ActivityDateKey/100 >= InactiveData.InactiveDate/100) and (mainData.ActivityDateKey/10000 = InactiveData.InactiveDate/10000) then ‘Y’ else ‘N’ end as AcitveYTD
From History_AutoParkingMemberShip mainData
Left Outer Join
(
     Select MemberID, Max(ActivityDateKey) as InactiveDate
     From AutoParkinigMembership
     Where IsActive = ‘N’ 
     Group by MemberID
) InactiveData on InactiveData.MemberID = MainData.MemberID

But this query is not 100% producing correct result. Because I need to also find if any situation when the IsActive value = ‘Y’ then the previous values wherever I have updated the ActiveYTD value ‘Y’ needed to update to ‘N’.

MemberID  MemberShipDate  ActivityDateKey  ActivityMonthEndDateKEy  InactiveDate, IsActive  ActiveYTD

1001      2019/06/17      20220101         20220131                 Null          Y     Y
1001      2019/06/17      20220102         20220131                 Null          Y     Y 
...       ....             ...               ...                    ...          ...    ...
1001      2019/06/17      20220201         20220228                 Null          Y     Y 
1001      2019/06/17      20220215         20220228                 20220420      N     N
...       ....             ...               ...                    ...          ...    ...
1001      2019/06/17      20220420         20220430                 20220420      N     N 
1001      2019/06/17      20220421         20220430                 Null          Y     Y
1001      2019/06/17      20220422         20220430                 Null          Y     Y

Now you see the above data where ActiveYTD field should never had value = 'N' because this member 1001 become active again from April 21st 2022. This is little confusion. Could someone give your thoughts how to improve the query.


Solution

  • You can do that using Window Function.

    The idea is to find the last IsActive value with 'Y' of that year sorted by ActivityDateKey.

    Any row previous to that should have ActiveYTD as 'Y'. If there is any record with IsActive='N' after last 'Y', mark those as 'N'.

    Below Window Function will give the Count of 'Y' present till the current row.

    COUNT(CASE WHEN IsActive='Y' THEN 1 END) 
    OVER( PARTITION BY MemberID,ActivityDateKey/10000
    ORDER BY ActivityDateKey Desc ROWS UNBOUNDED PRECEDING )
    

    Final Query:

    SELECT 
    tab.*
    ,CASE WHEN 
    COUNT(CASE WHEN IsActive='Y' THEN 1 END) 
    OVER( PARTITION BY MemberID,ActivityDateKey/10000
    ORDER BY ActivityDateKey Desc ROWS UNBOUNDED PRECEDING ) >0 THEN 'Y' ELSE 'N' END ActiveYTD
    FROM AutoParkinigMembership tab
    ORDER BY ActivityDateKey  
    

    db<>fiddle: Try here

    Note: Above fiddle is in sqlserver-2014, but it should work in 2012 as well.

    Dataset:

    MemberID MemberShipDate ActivityDateKey ActivityMonthEndDateKEy IsActive
    1001 2019-06-17 20220102 20220131 Y
    1001 2019-06-17 20220201 20220228 Y
    1001 2019-06-17 20220215 20220228 Y
    1001 2019-06-17 20220420 20220430 N
    1001 2019-06-17 20220421 20220430 Y
    1001 2019-06-17 20220422 20220430 N

    Output:

    MemberID MemberShipDate ActivityDateKey ActivityMonthEndDateKEy IsActive ActiveYTD
    1001 2019-06-17 20220102 20220131 Y Y
    1001 2019-06-17 20220201 20220228 Y Y
    1001 2019-06-17 20220215 20220228 Y Y
    1001 2019-06-17 20220420 20220430 N Y
    1001 2019-06-17 20220421 20220430 Y Y
    1001 2019-06-17 20220422 20220430 N N