MS SQL Server
I have two tables with different accounts from the same customer:
Table1:
ID | ACCOUNT | FROM | TO |
---|---|---|---|
1 | A | 01.10.2019 | 01.12.2019 |
1 | A | 01.02.2020 | 09.09.9999 |
and table2:
ID | ACCOUNT | FROM | TO |
---|---|---|---|
1 | B | 01.12.2019 | 01.01.2020 |
As result I want a table that summarize the story of this costumer and shows when he had an active account and when he doesn’t.
Result:
ID | FROM | TO | ACTIV Y/N |
---|---|---|---|
1 | 01.10.2019 | 01.01.2020 | Y |
1 | 02.01.2020 | 31.01.2020 | N |
1 | 01.02.2020 | 09.09.9999 | Y |
Can someone help me with some ideas how to proceed?
Advertisement
Answer
This is the typical gaps and island problem, and it’s not usually easy to solve.
You can achieve your goal using this query, I will explain it a little bit.
You can test on this db<>fiddle.
First of all… I have unified your two tables into one to simplify the query.
-- ##table1 select 1 as ID, 'A' as ACCOUNT, convert(date,'2019-10-01') as F, convert(date,'2019-12-01') as T into ##table1 union all select 1 as ID, 'A' as ACCOUNT, convert(date,'2020-02-01') as F, convert(date,'9999-09-09') as T -- ##table2 select 1 as ID, 'B' as ACCOUNT, convert(date,'2019-12-01') as F, convert(date,'2020-01-01') as T into ##table2 -- ##table3 select * into ##table3 from ##table1 union all select * from ##table2
You can then get your gaps and island using, for example, a query like this.
It combines recursive cte to generate a calendar (cte_cal
) and lag
and lead
operations to get the previous/next record information to build the gaps.
with cte_cal as ( select min(F) as D from ##table3 union all select dateadd(day,1,D) from cte_cal where d < = '2021-01-01' ), table4 as ( select t1.ID, t1.ACCOUNT, t1.F, isnull(t2.T, t1.T) as T, lag(t2.F, 1,null) over (order by t1.F) as SUP from ##table3 t1 left join ##table3 t2 on t1.T=t2.F ) select ID, case when T = D then F else D end as "FROM", isnull(dateadd(day,-1,lead(D,1,null) over (order by D)),'9999-09-09') as "TO", case when case when T = D then F else D end = F then 'Y' else 'N' end as "ACTIV Y/N" from ( select * from cte_cal c cross apply ( select t.* from table4 t where t.SUP is null and ( c.D = t or c.D = dateadd(day,1,t.T) ) ) t union all select F, * from table4 where T = '9999-09-09' ) p order by 1 option (maxrecursion 0)
Dates like '9999-09-09'
must be treated like exceptions, otherwise I would have to create a calendar until that date, so the query would take long time to resolve.