سؤال

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

For each customer's card, I need to have all dates between card_issue_date and card_expire_date. So the output for the first record would be like this :

customer_num    |  card_number   |  card_issue_date 
----------------------------------------------------
  1             |  x             |  1/1/2019        
  1             |  x             |  1/2/2020        
  1             |  x             |  1/3/2020       
  1             |  x             |  1/4/2020      

IS there any function or something else that can help me out with this?

Thanks in advance

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

المحلول

This should get you started:

declare
  s date := date'2020-01-01';
  e date := date'2020-01-04';
  ndays number;
begin
  ndays := e-s+1;
  for i in 1..ndays loop
    dbms_output.put_line (s);
    s := s+1;
  end loop;
end;
/

01-JAN-2020
02-JAN-2020
03-JAN-2020
04-JAN-2020

PL/SQL procedure successfully completed.

Just use simple date arithmetic: the difference between two dates is the number of days between the two:

date'2020-01-04' - date'2020-01-01' + 1

The above returns 4 (the +1 is so as to also include the last day, since this is what you describe in your example).

Then the rest is just a loop over the number of days, incrementing the start date by one day on each loop:

s := s + 1;

The next stage is to turn that into a usable function. First we need a type:

create or replace type date_array as table of date;
/

This defines an array of dates as is used as the output of the pipelined function:

create or replace function dates_between (s date, e date)
return date_array
pipelined
is
  d date := s;
begin
  for i in 1..e-s+1 loop
    pipe row(d);
    d := d + 1;
  end loop;
end;
/
show errors

This function returns the list of dates between two dates (including the end date). Like this:

SQL> select * from table (dates_between (date'2020-02-23',date'2020-03-05'));

COLUMN_VALUE
-----------------
23-FEB-2020
24-FEB-2020
25-FEB-2020
26-FEB-2020
27-FEB-2020
28-FEB-2020
29-FEB-2020
01-MAR-2020
02-MAR-2020
03-MAR-2020
04-MAR-2020
05-MAR-2020

12 rows selected.

Let's try that with your example. First let's create and populate the test table:

drop table test_table purge; 
create table test_table (
  customer_num      NUMBER,
  card_number       char(2),
  card_issue_date   DATE,
  card_expire_date  DATE
);
insert into test_table values (1,'x',to_date('1/1/2020 ','MM-DD-YYYY')  ,to_date('1/4/2020','MM-DD-YYYY'));
insert into test_table values (1,'y',to_date('1/1/2020 ','MM-DD-YYYY')  ,to_date('1/4/2020','MM-DD-YYYY'));
insert into test_table values (1,'z',to_date('1/11/2020','MM-DD-YYYY')  ,to_date('1/15/2020','MM-DD-YYYY'));
insert into test_table values (1,'b',to_date('1/11/2020','MM-DD-YYYY')  ,to_date('1/20/2020','MM-DD-YYYY'));
insert into test_table values (1,'w',to_date('1/12/2020','MM-DD-YYYY')  ,to_date('1/17/2020','MM-DD-YYYY'));
insert into test_table values (1,'a',to_date('1/18/2020','MM-DD-YYYY')  ,to_date('2/12/2020','MM-DD-YYYY'));
commit;

(Noticed I corrected the first date to match your description).

Here is the query that gives the result you describe:

select customer_num, card_number, column_value as card_date
from test_table, table (dates_between (card_issue_date,card_expire_date));

which returns:

CUSTOMER_NUM CARD_N CARD_DATE
------------ ------ -----------------
           1 x      01-JAN-2020
           1 x      02-JAN-2020
           1 x      03-JAN-2020
           1 x      04-JAN-2020
           1 y      01-JAN-2020
           1 y      02-JAN-2020
           1 y      03-JAN-2020
           1 y      04-JAN-2020
           1 z      11-JAN-2020
           1 z      12-JAN-2020
           1 z      13-JAN-2020
           1 z      14-JAN-2020
           1 z      15-JAN-2020
           1 b      11-JAN-2020
           1 b      12-JAN-2020
           1 b      13-JAN-2020
           1 b      14-JAN-2020
           1 b      15-JAN-2020
           1 b      16-JAN-2020
           1 b      17-JAN-2020
           1 b      18-JAN-2020
           1 b      19-JAN-2020
           1 b      20-JAN-2020
           1 w      12-JAN-2020
           1 w      13-JAN-2020
           1 w      14-JAN-2020
           1 w      15-JAN-2020
           1 w      16-JAN-2020
           1 w      17-JAN-2020
           1 a      18-JAN-2020
           1 a      19-JAN-2020
           1 a      20-JAN-2020
           1 a      21-JAN-2020
           1 a      22-JAN-2020
           1 a      23-JAN-2020
           1 a      24-JAN-2020
           1 a      25-JAN-2020
           1 a      26-JAN-2020
           1 a      27-JAN-2020
           1 a      28-JAN-2020
           1 a      29-JAN-2020
           1 a      30-JAN-2020
           1 a      31-JAN-2020
           1 a      01-FEB-2020
           1 a      02-FEB-2020
           1 a      03-FEB-2020
           1 a      04-FEB-2020
           1 a      05-FEB-2020
           1 a      06-FEB-2020
           1 a      07-FEB-2020
           1 a      08-FEB-2020
           1 a      09-FEB-2020
           1 a      10-FEB-2020
           1 a      11-FEB-2020
           1 a      12-FEB-2020

55 rows selected.

نصائح أخرى

Using just SQL and using Tom's as_many_rows trick(adjust as required):

with as_many_rows_as_you_need
as
( select level l
  from dual
  connect by level <= 100
) 
select customer_num cust, card_number card, (a.card_issue_date + l) dat
from   as_many_rows_as_you_need
     , test_table a
where  a.card_issue_date + l <= a.card_expire_date
and    a.customer_num = 1
--and    a.card_number  = 'x'
order by card_number, (a.card_issue_date + l)
;

Using above table definition and data, results in(cut-off...):

CUST    CARD    DAT
1   a   19-jan-20
1   a   20-jan-20
1   a   21-jan-20
1   b   12-jan-20
1   b   13-jan-20
1   b   14-jan-20
1   b   15-jan-20
1   b   16-jan-20
1   b   17-jan-20
1   b   18-jan-20
1   b   19-jan-20
1   b   20-jan-20
1   w   13-jan-20
1   w   14-jan-20
1   w   15-jan-20
1   w   16-jan-20
1   w   17-jan-20
...
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى dba.stackexchange
scroll top