Oracle SQL: Fill in Missing Dates

ORACLE SQL: Fill in missing dates

You can get the 30-day period from SYSDATE as follows (I assume you want to include SYSDATE?):

WITH mydates AS (
SELECT TRUNC(SYSDATE) - 1 + LEVEL AS due_date FROM dual
CONNECT BY LEVEL <= 31
)

Then use the above to do a LEFT JOIN with your query (perhaps not a bad idea to put your query in a CTE as well):

WITH mydates AS (
SELECT TRUNC(SYSDATE) - 1 + LEVEL AS due_date FROM dual
CONNECT BY LEVEL <= 31
), myorders AS (
select
(case when trunc(so.revised_due_date) <= trunc(sysdate)
then trunc(sysdate) else trunc(so.revised_due_date) end) due_date,
(case
when (case when sp.pr_typ in ('VV','VD') then 'DVD' when sp.pr_typ in ('RD','CD')
then 'CD' end) = 'CD'
and (case when so.tec_criteria in ('PI','MC')
then 'XX' else so.tec_criteria end) = 'OF'
then sum(so.revised_qty_due)
end) CD_OF_VOLUME
from shop_order so
left join scm_prodtyp sp
on so.prodtyp = sp.prodtyp
where so.order_type = 'MD'
and so.plant = 'W'
and so.status_code between '4' and '8'
and trunc(so.revised_due_date) <= trunc(sysdate)+30
group by trunc(so.revised_due_date), so.tec_criteria, sp.pr_typ
order by trunc(so.revised_due_date)
)
SELECT mydates.due_date, myorders.cd_of_volume
FROM mydates LEFT JOIN myorders
ON mydates.due_date = myorders.due_date;

If you want to show a zero on "missing" dates instead of a NULL, use COALESCE(myorders.cd_of_volume, 0) AS cd_of_volume above.

Oracle SQL - Populate Missing dates and populate last available date values for a given ID

create table test_roll
( dte date,
id int,
value number(9,2)
);

insert into test_roll values(to_date('2/4/17','MM/DD/YYYY'),3, 4.4);
insert into test_roll values(to_date('2/4/17','MM/DD/YYYY'),9, 6.2);
insert into test_roll values(to_date('2/9/17','MM/DD/YYYY'),3, 4.7);
insert into test_roll values(to_date('2/9/17','MM/DD/YYYY'),4, 7.4);
insert into test_roll values(to_date('2/9/17','MM/DD/YYYY'),9, 9.4);
insert into test_roll values(to_date('2/11/17','MM/DD/YYYY'),3, 9.7);
insert into test_roll values(to_date('2/11/17','MM/DD/YYYY'),7, 12.4);

commit;

select * from test_roll order by dte, id;

DTE ID VALUE
--------- ---------- ----------
04-FEB-17 3 4.4
04-FEB-17 9 6.2
09-FEB-17 3 4.7
09-FEB-17 4 7.4
09-FEB-17 9 9.4
11-FEB-17 3 9.7
11-FEB-17 7 12.4

SELECT DTE, ID, LAST_VALUE(value) IGNORE NULLS OVER (PARTITION BY ID ORDER BY ID,DTE) AS value
FROM (
WITH DATES AS
(
select DISTINCT ID, (MIN_DTE + LEVEL - 1) REAL_DTE
FROM (
select ID, min(DTE) as MIN_DTE,
greatest( max(dte), (select max(dte) - 1 from test_roll)) as MAX_DTE
FROM test_roll
group by ID )
CONNECT BY LEVEL <= MAX_DTE - MIN_DTE + 1
)
SELECT D.REAL_DTE AS DTE,
D.ID as ID,
t.value as value
FROM DATES D
LEFT JOIN test_roll t ON t.ID=D.ID AND D.REAL_DTE=t.DTE
) ROLLED
ORDER BY DTE, ID;

DTE ID VALUE
--------- ---------- ----------
04-FEB-17 3 4.4
04-FEB-17 9 6.2
05-FEB-17 3 4.4
05-FEB-17 9 6.2
06-FEB-17 3 4.4
06-FEB-17 9 6.2
07-FEB-17 3 4.4
07-FEB-17 9 6.2
08-FEB-17 3 4.4
08-FEB-17 9 6.2
09-FEB-17 3 4.7
09-FEB-17 4 7.4
09-FEB-17 9 9.4
10-FEB-17 3 4.7
10-FEB-17 4 7.4
10-FEB-17 9 9.4
11-FEB-17 3 9.7
11-FEB-17 7 12.4

How do I fill in missing dates by group in Oracle with changing count value

with start_params as (
select
to_date('01/01/2020', 'MM/DD/YYYY') as start_date,
60 numdays
from dual
),
colors as (
select to_date('1/28/2020 09:29', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color, 1 color_count from dual union
select to_date('2/3/2020 07:04', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color, 5 color_count from dual union
select to_date('2/6/2020 12:11', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color, 11 color_count from dual union
select to_date('2/11/2020 17:15', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color, 4 color_count from dual union
select to_date('2/15/2020 03:46', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color, 6 color_count from dual union
select to_date('1/16/2020 14:52', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 7 color_count from dual union
select to_date('1/19/2020 22:30', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 32 color_count from dual union
select to_date('1/23/2020 05:17', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 16 color_count from dual union
select to_date('1/28/2020 18:35', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 24 color_count from dual union
select to_date('1/31/2020 15:38', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 41 color_count from dual union
select to_date('2/2/2020 16:01', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color, 11 color_count from dual
),
upd_colors as (
select
(select start_date from start_params) color_date,
color,
min(color_count) keep(dense_rank first order by color_date) color_count
from colors
group by color
union
select trunc(color_date), color, color_count from colors
),
dates as (
select dat, color
from (
select start_date + numtodsinterval(level-1, 'DAY') dat
from start_params connect by level <= numdays
), (select distinct color from colors)
)
select d.dat, d.color,
nvl(c.color_count, lag(c.color_count ignore nulls) over (partition by d.color order by d.dat)) color_count
from dates d, upd_colors c
where c.color_date(+) = d.dat
and c.color(+) = d.color
order by color, dat

fiddle

Oracle: select missing dates

Something like this (assuming your table is named your_table and the date column is named the_date):

with date_range as (
select min(the_date) as oldest,
max(the_date) as recent,
max(the_date) - min(the_date) as total_days
from your_table
),
all_dates as (
select oldest + level - 1 as a_date
from date_range
connect by level <= (select total_days from date_range)
)
select ad.a_date
from all_dates ad
left join your_table yt on ad.a_date = yt.the_date
where yt.the_date is null
order by ad.a_date;

Edit:

the WITH clause is called a "common table expression" and is equivalent to a derived table ("inline view").

It's similar to

select * 
from (
.....
) all_dates
join your_table ...

The second CTE simply creates a list of dates "on-the-fly" using a undocumented feature of Oracle's connect by implementation.

Re-using a select (like I did with calculating the first and last date) is a bit easier (and IMHO more readable) than using derived tables.

Edit 2:

This can be done with a recursive CTE as well:

with date_range as (
select min(the_date) as oldest,
max(the_date) as recent,
max(the_date) - min(the_date) as total_days
from your_table
),
all_dates (a_date, lvl) as (
select oldest as a_date, 1 as lvl
from date_range
union all
select (select oldest from date_range) + lvl, lvl + 1
from all_dates
where lvl < (select total_days from date_range)
)
select ad.a_date, lvl
from all_dates ad
left join your_table yt on ad.a_date = yt.the_date
where yt.the_date is null
order by ad.a_date;

Which should work in all DBMS supporting recursive CTEs (PostgreSQL and Firebird - being more standard compliant - do need the recursive keyword though).

Note the hack select (select oldest from date_range) + lvl, lvl + 1 in the recursive part. This should not be necessary, but Oracle still has some bugs with regards to DATEs in a recursive CTE. In PostgreSQL the following works without problems:

....
all_dates (a_date, lvl) as (
select oldest as a_date, 0 as lvl
from date_range
union all
select a_date + 1, lvl + 1
from all_dates
where lvl < (select total_days from date_range)
)
....

How do I fill in missing dates by group in Oracle

You can use Recursive Subquery Factoring to generate intervals:

with tbl as (
select to_date('1/28/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color from dual union
select to_date('2/3/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color from dual union
select to_date('2/6/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'red' as color from dual union
select to_date('4/16/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color from dual union
select to_date('4/19/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color from dual union
select to_date('4/23/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color from dual union
select to_date('5/2/2020 11:51', 'MM/DD/YYYY HH24:MI') as color_date, 'blue' as color from dual
),
tbl_min_max as (
select t.color, min(trunc(color_date)) begin_interval, max(trunc(color_date)) end_interval from tbl t group by t.color
),
tbl_interval(color, begin_interval, end_interval, color_date) as (
select color, begin_interval, end_interval, begin_interval from tbl_min_max
union all
select color, begin_interval, end_interval, color_date + 1 from tbl_interval where color_date < end_interval
)
select
t.color, t.color_date
from
tbl_interval t
order by
t.color, t.color_date


Related Topics



Leave a reply



Submit