Простой способ получить данные за 2 года, даже если мы пропустим несколько месяцев - PullRequest
0 голосов
/ 12 февраля 2019

Мне нужно выбрать все отображаемые 24 месяца, даже если месяцы отсутствуют, с соответствующим значением продаж как нулевым.Это то, что я придумал. Как вы можете видеть, нам не хватает id и custname.Мне нужны id и custname только с продажами, равными NULL, как показано на скриншоте ниже

select CUSTNAME, reportdate, sales, mth
from (  select add_months(date '2017-01-01', level - 1) as mth 
  from dual 
  connect by level <= 24)mo
  left outer join oracle_tbl dc on mo.mth = trunc(reportdate, 'MM')
     order by mth

Я также попробовал следующее, и ничто просто не работает (то есть), давая нули для CustName иидентификатор с отсутствующими датами

 WITH mydates AS (
    select LAST_DAY(add_months(date '2017-01-01', level - 1)) as mth, min_id,min_custname
  from (
  select min(id) as min_id, min(CUSTNAME) as min_custname
  from my_oracle_tbl 
  )
  connect by level <= 24)
  select
  nvl(t.id, a.min_id)id,
  nvl(t.CUSTNAME,a.min_custname)CUSTNAME, a.mth, t.sales
   from mydates a left join my_oracle_tbl t on a.mth= LAST_DAY(t.reporttdate)
  where
  t.id=12345;


  select CUSTNAME, reportdate, sales, mth
  from( SELECT CUSTNAME, reportdate, sales, mth FROM my_oracle_tbl
  WHERE id=123 )
 myTotals
       right outer join
       (select LAST_DAY(date '2017-01-01' + numtoyminterval(level-1,'month')) MonthName
          from dual
       connect by level <= 24) ALLMONTHS
       on( myTotals.mm = allmonths.MonthName )

[! [Вот так я получаю .. пропущенные идентификатор и имя пользователя] [1]] [1]

[! [это то, что мне нужно] [2]] [2]

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Предположим, у нас есть следующие таблицы: {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

0 голосов
/ 12 февраля 2019

Вы не сказали точно, что не так, но вы упорядочиваете по строковому значению, поэтому вы получите январь 2017 года, затем январь 2018 года, затем февраль 2017 года ...

Сохраните даты в качестве фактических датпока вам не нужно отобразить их в фиксированном формате (или пусть ваш клиент сделает это).Для сравнения:

select dc.CUSTNAME, dc.reportdate, dc.sales, mo.mth
from (
  select add_months(date '2017-01-01', level - 1) as mth 
  from dual 
  connect by level <= 24
) mo
left outer join oracle_tbl dc on mo.mth = trunc(dc.reportdate, 'MM')
order by mo.mth

, где trunc(reportdate, 'MM') усекает дату отчета до первого дня месяца;или с диапазоном дат (который более удобен для индексов):

select dc.CUSTNAME, dc.reportdate, dc.sales, mo.mth
from (
  select add_months(date '2017-01-01', level - 1) as mth 
  from dual 
  connect by level <= 24
) mo
left outer join oracle_tbl dc
on dc.reportdate >= mo.mth
and dc.reportdate < add_months(mo.mth, 1)
order by mo.mth

В любом случае вы получите mth в качестве даты, которую вы затем можете отформатировать так, как хотите для отображения - если вы на самом делехочу отобразить это вообще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...