Предположим, у нас есть следующие таблицы: {1} ДАТЫ, содержащие последний день всех необходимых вам месяцев, и {2} ПРОДАЖИ, содержащие продажи для клиентов (не за каждый месяц).
create table dates ( month_end_date )
as
select add_months(date '2017-01-01', level - 1) - 1
from dual
connect by level <= 24 ;
create table sales (customerid, custname, sales, date_of_sale )
as
select *
from (
select
case when mod( level, 3 ) = 0 then 1 end as customerid
, case when mod( level, 3 ) = 0 then 'test' end
, case when mod( level, 3 ) = 0 then trunc( dbms_random.value() * 10000 ) end
, case when mod( level, 3 ) = 0 then add_months(date '2017-01-01', level - 1) end
from dual connect by level <= 24
)
where customerid is not null ;
таблица ПРОДАЖ
-- SALES colum: random values!
-- sales values recorded every third month
SQL> select * from sales ;
CUSTOMERID CUST SALES DATE_OF_S
---------- ---- ---------- ---------
1 test 5764 01-MAR-17
1 test 3937 01-JUN-17
1 test 9926 01-SEP-17
1 test 3045 01-DEC-17
1 test 598 01-MAR-18
1 test 325 01-JUN-18
1 test 2590 01-SEP-18
1 test 5803 01-DEC-18
8 rows selected.
таблица ДАТ
SQL> select * from dates ;
MONTH_END
---------
31-DEC-16
31-JAN-17
28-FEB-17
31-MAR-17
30-APR-17
...
31-JUL-18
31-AUG-18
30-SEP-18
31-OCT-18
30-NOV-18
24 rows selected.
Следующий запрос должен дать вам кое-что для работы ... В CROSS JOIN вы найдете все комбинации индексов пользователя + имена клиентов и month_end_dates,LEFT JOIN выводит все значения NULL, которые вам нужны.(Обратите внимание, что мы вызываем LAST_DAY () в условии соединения.) Вы также можете использовать «SELECT ... CONNECT BY ...» для генерации всех MONTH_END_DATE (как вы сделали в своем собственном запросе)
select CM.customerid, CM.custname, S.sales, CM.month_end_date
from (
select *
from (
( select unique customerid, custname from sales )
cross join
( select month_end_date from dates ) -- <- data could be "generated"
)
) CM left join sales S on CM.month_end_date = last_day( S.date_of_sale )
order by CM.month_end_date
;
Результат
CUSTOMERID CUST SALES MONTH_END
---------- ---- ---------- ---------
1 test NULL 31-DEC-16
1 test NULL 31-JAN-17
1 test NULL 28-FEB-17
1 test 5764 31-MAR-17
1 test NULL 30-APR-17
1 test NULL 31-MAY-17
1 test 3937 30-JUN-17
1 test NULL 31-JUL-17
1 test NULL 31-AUG-17
1 test 9926 30-SEP-17
1 test NULL 31-OCT-17
1 test NULL 30-NOV-17
1 test 3045 31-DEC-17
1 test NULL 31-JAN-18
1 test NULL 28-FEB-18
1 test 598 31-MAR-18
1 test NULL 30-APR-18
1 test NULL 31-MAY-18
1 test 325 30-JUN-18
1 test NULL 31-JUL-18
1 test NULL 31-AUG-18
1 test 2590 30-SEP-18
1 test NULL 31-OCT-18
1 test NULL 30-NOV-18
-- caution: sale for 01-DEC-18 "chopped off"
24 rows selected.
dbfiddle здесь
РЕДАКТИРОВАТЬ (ограничить диапазон дат, фильтр по клиенту)
Чтобы иметь дело с более чем одним клиентом и сузить диапазон дат (из набора результатов), добавьте условие в предложение ON для LEFT JOIN, а затем добавьте еще несколько условий для предложения WHERE, например,
SALES (тест)таблица CTAS
-- New SALES table for testing
-- 5 customers, 1 sale every 5 months (per customer)
create table sales (customerid, custname, sales, date_of_sale )
as
select *
from (
select
mod( level, 5 ) + 1
, 'test' || to_char( mod( level, 5 ) + 1 )
, trunc( dbms_random.value() * 10000 )
, add_months(date '2017-01-01', level - 1)
from dual connect by level <= 24
)
;
ПРОДАЖА данных
SQL> select * from sales;
CUSTOMERID CUSTNAME SALES DATE_OF_SALE
2 test2 5594 01-JAN-17
3 test3 6398 01-FEB-17
4 test4 2072 01-MAR-17
5 test5 4269 01-APR-17
1 test1 9435 01-MAY-17
2 test2 6984 01-JUN-17
3 test3 5735 01-JUL-17
4 test4 9549 01-AUG-17
5 test5 9686 01-SEP-17
1 test1 9193 01-OCT-17
2 test2 1702 01-NOV-17
3 test3 8277 01-DEC-17
4 test4 8235 01-JAN-18
5 test5 7596 01-FEB-18
1 test1 5507 01-MAR-18
2 test2 6267 01-APR-18
3 test3 5708 01-MAY-18
4 test4 755 01-JUN-18
5 test5 3966 01-JUL-18
1 test1 5167 01-AUG-18
2 test2 6819 01-SEP-18
3 test3 9268 01-OCT-18
4 test4 1844 01-NOV-18
5 test5 1085 01-DEC-18
24 rows selected.
Запрос
-- sales for customer (id) 3, between 30 Apr 2018 and 31 Dec 2018
select CM.customerid, CM.custname, S.sales, CM.month_end_date
from (
select *
from (
( select unique customerid, custname from sales )
cross join
( select month_end_date from dates )
)
) CM
left join sales S
on CM.month_end_date = last_day( S.date_of_sale )
and CM.customerid = S.customerid
where CM.customerid = 3
and CM.month_end_date > date '2018-04-30'
and CM.month_end_date < date '2018-12-31'
order by CM.month_end_date
;
-- result
CUSTOMERID CUSTNAME SALES MONTH_END_DATE
3 test3 5708 31-MAY-18
3 test3 NULL 30-JUN-18
3 test3 NULL 31-JUL-18
3 test3 NULL 31-AUG-18
3 test3 NULL 30-SEP-18
3 test3 9268 31-OCT-18
3 test3 NULL 30-NOV-18
dbfiddle