Query to find the “Number of days” a customer has an active card between a specified interval

dba.stackexchange https://dba.stackexchange.com/questions/285027

سؤال

I have a table with below structure :

create table test_table
(
  customer_num      NUMBER,
  card_number       char(2),
  card_issue_date   DATE,
  card_expire_date  DATE
)

Example data is:

customer_num    |  card_number   |  card_issue_date | card_expire_date
--------------------------------------------------------------------------
  1             |  x             |  1/1/2019        | 1/4/2020
  1             |  y             |  1/1/2020        | 1/4/2020
  1             |  z             |  1/11/2020       | 1/15/2020 
  1             |  b             |  1/11/2020       | 1/20/2020
  1             |  w             |  1/12/2020       | 1/17/2020
  1             |  a             |  1/18/2020       | 2/12/2020

I have a calendar table with below structure :

create table dimdate
(
  datekey   DATE,
  <other columns related to date>
)

The above table has all dates .

For each custoemr_num we need to find the total number of days he has an active card between '1/1/2020 ' and '1/31/2020'. We need to take some important points into consideration here :

1)We need to pay attention that for some cards such as card number x and a the issue_date might be smaller than '1/1/2020 ' or bigger than '1/31/2020'.

2)We have to pay attention to overlaps. There might be overlap between issue_date and expire_date of different cards (b,w,z,a).

First we thought that we could calculate the number of active days for each card and then subtract the overlaps from the final result and
what we wrote so far is this :

select t.customer_num,
       t.card_number,
       t.card_issue_date,
       t.card_expire_date,
       lead(t.card_issue_date) over(partition by t.customer_num order by t.card_issue_date) next_card_issue_date,
       case
         when t.card_issue_date < to_date('1/1/2020', 'mm/dd/yyyy') and
              t.card_expire_date > to_date('1/31/2020', 'mm/dd/yyyy') then
          to_date('1/31/2020', 'mm/dd/yyyy') -
          to_date('1/1/2020', 'mm/dd/yyyy')
       
         when t.card_issue_date < to_date('1/1/2020', 'mm/dd/yyyy') then
          t.card_expire_date - to_date('1/1/2020', 'mm/dd/yyyy')
       
         when t.card_expire_date > to_date('1/31/2020', 'mm/dd/yyyy') then
          to_date('1/31/2020', 'mm/dd/yyyy') - t.card_issue_date
       
         else
          t.card_expire_date - t.card_issue_date
       
       end as card_active_days

  from test_table t
 where t.card_issue_date <= to_date('1/31/2020', 'mm/dd/yyyy')
   and t.card_expire_date >= to_date('1/1/2020', 'mm/dd/yyyy')

but we could not handle the overlaps. I was wondering if you could help me here .

Thanks in advance

هل كانت مفيدة؟

المحلول

I think I have found a good solution for this. Could you tell me if there are better ways other than this one?

select a.customer_num, count(distinct b.datekey) num_of_days
  from (select t.customer_num,
               t.card_number,
               t.card_issue_date,
               t.card_expire_date
          from test_table t
         where t.card_issue_date <= to_date('1/31/2020', 'mm/dd/yyyy')
           and t.card_expire_date >= to_date('1/1/2020', 'mm/dd/yyyy')) a
 inner join dimdate b
    on b.datekey between a.card_issue_date and a.card_expire_date
   and b.datekey between to_date('1/1/2020', 'mm/dd/yyyy') and
       to_date('1/31/2020', 'mm/dd/yyyy')
 group by a.customer_num
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى dba.stackexchange
scroll top