即使我们错过了几个月的时间,也可以轻松获取2年的月份数据

时间:2019-02-11 23:50:26

标签: sql oracle

即使缺少对应的销售值为空的月份,我也需要获取显示的全部24个月。这就是我想出的。 您可以看到我们缺少ID和客户名称。我需要将sales设为空的id和客户名称,如下面的屏幕截图所示

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

我也尝试了以下方法,但似乎没有任何效果(例如,为客户名和ID缺少日期提供了空值

 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 )

[![这就是我得到的方式。缺少ID和客户名称] [1]] [1]

[![这就是我需要的] [2]] [2]

2 个答案:

答案 0 :(得分:1)

您还没有确切地说出问题所在,但您是按字符串值进行订购的,所以您将得到2017年1月,然后是2018年1月,然后是2017年2月...

保留日期作为实际日期,直到您需要以固定格式显示它们(或让您的客户这样做)为止。包括进行比较。

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作为日期,然后可以格式化该日期(如果您真的想显示它的话)。

答案 1 :(得分:0)

假设我们有以下表格:{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. 

DATES表

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 here

编辑(限制日期范围,按客户过滤)

要与一个以上的客户打交道,并缩小(结果集的)日期范围,请在LEFT JOIN的ON子句中添加一个条件,然后在WHERE子句中添加更多条件,例如

销售(测试)表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