Please - don't name columns "start" or "end". These are reserved words in Oracle! Qualify them with the type - e.g. start_date, end_datetime, etc.
Onto your question:
You can create a list of "last days of the month" by:
- Generating a row for each month using the connect by level trick
- Provide a start date for this list and get all the other dates using add_months
- Join the results of this to TT where the date is between the start and end:
CREATE TABLE tt (skey int, start_dt date, end_dt date);
alter session set nls_date_format = 'dd/mm/yyyy';
INSERT INTO tt VALUES (1, '31/01/2016', '31/03/2016');
INSERT INTO tt VALUES (2, '29/02/2016', '31/03/2016');
INSERT INTO tt VALUES (3, '29/02/2016', '30/04/2016');
INSERT INTO tt VALUES (4, '31/03/2016', '30/04/2016');
INSERT INTO tt VALUES (5, '29/02/2016', '30/06/2016');
with rws as (
select add_months(date'2016-01-31', rownum-1) dt from dual
connect by level <= (select months_between(max(end_dt), min(start_dt)) from tt)
)
select dt, skey
from rws
join tt
on dt between start_dt and end_dt
where skey in (1, 2, 4)
order by 2, 1;
DT SKEY
31/01/2016 1
29/02/2016 1
31/03/2016 1
29/02/2016 2
31/03/2016 2
31/03/2016 4
30/04/2016 4