I have some dates in table over of a two years like this as a example date
01-jan-2012
02-jan-2012
04-jan-2012
05-jan-2012
06-jan-2012
07-jan-2012
09-jan-2012
11-jan-2012
.
.
.
01-DEC-2012
I think you have noticed that there a missing date of 03-jan-2012 and 08-jan-2012 and the same criteria with all dates.My Question is that is there any way in oracle to find the missing dates.Plz Help !
This will get you all missing days for one year (SQL Fiddle).
all_dates generates a list of all dates of 2012 (adjust as required), and the LEFT JOIN checking for IS NULL eliminates those dates that exist in your source-table.
WITH all_dates AS (
SELECT TO_DATE('01-jan-2012') + ROWNUM - 1 AS d
FROM dual
CONNECT BY ROWNUM <= ADD_MONTHS(TO_DATE('01-jan-2012'), 12 ) - TO_DATE('01-jan-2012')
)
SELECT all_dates.d
FROM all_dates
LEFT JOIN t ON ( t.d = all_dates.d )
WHERE t.d IS NULL
ORDER BY all_dates.d
;
Make sure to use a bind variable instead of hard-coding the date three times.
You can generate sequence of date range you needed, then use LEFT JOIN to find missed dates.
Almost the same answer as #Peter, but a very slightly different version.
select all_dates.date_ missing_dates from
(select to_date('01-Jan-14') + level - 1 date_
from dual connect by level <= 365) all_dates
left join
((select to_date('01-Jan-14') + level - 1 date_
from dual connect by level <= 365)
minus
(select to_date(round (dbms_random.value (1, 28))
|| '-'
|| round (dbms_random.value (01, 12))
|| '-'
|| round (dbms_random.value (2014, 2014)),
'DD-MM-YYYY') + level - 1 random_date_1
from dual
connect by level <= 52)) transaction_data
on all_dates.date_ = transaction_data.date_
where transaction_data.date_ is null;
Related
with cte as
(
select to_date('01-JUN-2020','DD-MON-YYYY')+(level-1) DT
from dual
connect bY level<= 30
)
select *
from cte x
left outer join
(select date from time where emp in (1, 2)) a on x.dt = a.date
In this scenario I am trying to find the missing days that these persons didn't report to work... it works well for 1 person. I get back their missing days correctly. But when I add 2 persons.. I do not get back the correct missing days for them because I'm only joining on date I guess.
I would like to know how I can partition this data by the persons id and date to be able get accurate days that each were missing.
Please help, thanks.
You would typically cross join the list of dates with the list of persons, and then use not exists to pull out the missing person/date tuples:
with cte as (
select date '2020-06-01' + level - 1 dt
from dual
connect by level <= 30
)
select c.dt, e.emp
from cte c
cross join (select distinct emp from times) e
where not exists (
select 1
from times t
where t.emp = e.emp and t.dt = e.date
)
Note that this uses a literal date rather than to_date(), which is more appropriate here.
This gives the missing tuples for all persons at once. If you want just for a predefined list of persons, then:
with cte as (
select date '2020-06-01' + level - 1 dt
from dual
connect by level <= 30
)
select c.dt, e.emp
from cte c
cross join (select 1 emp from dual union all select 2 from dual) e
where not exists (
select 1
from times t
where t.emp = e.emp and t.dt = e.date
)
If you want to also see the "presence" dates, then use a left join rather than not exists, as in your original query:
with cte as (
select date '2020-06-01' + level - 1 dt
from dual
connect by level <= 30
)
select c.dt, e.emp, -- enumerate the relevant columns from "t" here
from cte c
cross join (select 1 emp from dual union all select 2 from dual) e
left join times t on t.emp = e.emp and t.dt = e.date
I have a table with following structure.
Note_title varchar2(100)
Note_created_on date
Now in a report, I want to show all notes created week-wise, So I implemented the following solution for it.
SELECT to_char(Note_created_on - 7/24,'ww')||'/'||to_char(Note_created_on - 7/24,'yyyy') as Week ,
nvl(COUNT(Note_title),'0') as AMOUNT
FROM Notes
GROUP BY to_char(Note_created_on - 7/24,'ww') ,
to_char(Note_created_on -7/24,'yyyy')
ORDER BY to_char(Note_created_on - 7/24,'ww') DESC
And i am getting correct output from it, But suppose week 42,45 do not have any created Note then its just missing it.
Sample Output:
WEEK AMOUNT
46/2018 3
44/2018 22
43/2018 45
41/2018 1
40/2018 2
39/2018 27
38/2018 23
So How can I get zero values for week 42,45 instead of leaving them out?
First you would need to generate all the weeks between each year, after that would left join with the Notes tables on the weeks and group by the weeks generated. Eg:
with weeks
as ( select level as lvl /*Assume 52 weeks in a calendar year..*/
from dual
connect by level <=52
)
,weeks_year
as (select distinct
b.lvl||'/'||trunc(Note_created_on,'YYYY') as week_year_val /*From the start of year in Note_created_on*/
from Notes a
join weeks b
on 1=1
)
SELECT a.week_year_val as Week
,COUNT(Note_title) as AMOUNT
FROM weeks_year a
LEFT JOIN Notes b
ON a.week_year_val=to_char(b.Note_created_on - 7/24,'ww')||'/'||to_char(b.Note_created_on - 7/24,'yyyy')
GROUP BY a.week_year_val
ORDER BY a.week_year_val DESC
If you want to perform this for the current year, you may use the following SQL statement which uses such a RIGHT JOIN as below :
SELECT d.week as Week,
nvl(COUNT(Note_title), '0') as AMOUNT
FROM Notes
RIGHT JOIN
(SELECT lpad(level,2,'0')|| '/' ||to_char(sysdate,'yyyy') as week,
'0' as amount FROM dual CONNECT BY level <= 53) d
ON
( d.week =
to_char(Note_created_on - 7 / 24, 'ww') ||'/'||to_char(Note_created_on - 7 / 24, 'yyyy') )
GROUP BY d.week
ORDER BY d.week DESC;
P.S. There's a common belief that a year is composed of 52 weeks, true but truncated :). So, I used 53,
Notice that select to_char( date'2016-12-31' - 7 / 24, 'ww') from dual yields 53 as a sample.
Rextester Demo
As mentioned by jarlh:
Create a list of weeks:
SELECT TO_CHAR(LEVEL, 'FM00')||'/2018' wk
FROM dual
CONNECT BY LEVEL <= 53
This query generates 53 rows, and level is just a number.. 1.. 2.. upto 53. We format it to become 01/2018, 02/2018.. 53/2018
If you plan to use this query in other years, you'd be better off making the year dynamic:
SELECT TO_CHAR(LEVEL, 'FM00')||TO_CHAR(sysdate-7/24,'/YYYY') wk
FROM dual
CONNECT BY LEVEL <= 53
(Credits to Barbaros for pointing out that the last day of any year is reported by Oracle as being in week 53, or said another way 7*52 = 364)
We left join the notes data onto it. I wasn't really clear on why you subtracted 7 hours from the date (time zone?) but I left it. I removed the complexity of the count, as you seem to only want the count of records in a particular week. I also removed the double to_char, because you can do it all in a single operation. One doesn't need to TO_CHAR(date, 'WW')||'/'||TO_CHAR(date,'YYYY') etc.. you just tochar with WW/YYYY as a format. Our query now looks like:
SELECT lst.wk as week, COALESCE(amt, 0) as amount FROM
(
SELECT TO_CHAR(LEVEL, 'FM00')||TO_CHAR(sysdate-7/24,'/YYYY') wk
FROM dual
CONNECT BY LEVEL <= 52
) lst
LEFT OUTER JOIN
(
SELECT
to_char(Note_created_on - 7/24,'ww/yyyy') as wk,
COUNT(*) as amt
FROM Notes
GROUP BY to_char(Note_created_on - 7/24,'ww/yyyy')
) dat
ON lst.wk = dat.wk
ORDER BY lst.wk
For weeks where there are no note, the left join records a null against that week, so we coalesce it to make it 0.
You can, of course, do the query in other ways (many ways), here's a compare:
SELECT lst.wk as week, COUNT(dat.wk) as amount FROM
(
SELECT TO_CHAR(LEVEL, 'FM00')||TO_CHAR(sysdate-7/24,'/YYYY') wk
FROM dual
CONNECT BY LEVEL <= 52
) lst
LEFT OUTER JOIN
(
SELECT
to_char(Note_created_on - 7/24,'ww/yyyy') as wk
FROM Notes
) dat
ON lst.wk = dat.wk
GROUP BY lst.wk
ORDER BY lst.wk
In this form we do the groupby/count after the join. By counting the dat.wk, which for some lst.wk might be NULL, we can omit the coalesce, because count(null) is 0
I have the following basic script, this shows me some current capacity loading in my production schedule.
select rl.duedate, rl.reservation_no resnr, qty
from gps_reservation_load rl
where rl.reservation_no in ('179459','179460','179461')
and rl.work_center_no in ('ALIN','AVD5','AVD9')
But, I want to show the DUEDATE as a date range from the sysdate to end of the year, e.g.
I have the following that gives me that range but, how can I combine the scripts to give me the result above?
select trunc(sysdate + rownum) dt
from DUAL connect by rownum < (to_date('01-JAN-2016', 'dd-mon-yyyy') - trunc(sysdate))
You just need to use outer join.
SELECT duedate_generated,reservation_no, qty, dt
from
(SELECT
trunc(sysdate + rownum) AS duedate_generated
FROM DUAL
connect by rownum < (to_date('01-JAN-2016', 'dd-mon-yyyy') - trunc(sydsate)
) d
OUTER JOIN
(select
duedate, reservation_no resnr, qty
from gps_reservation_load
where
reservation_no in ('179459','179460','179461')
and work_center_no in ('ALIN','AVD5','AVD9')
) r1
ON (d.duedate_generated = r1. due date)
I am trying to run a report to show a list of all dates within the past 90 days and then join that back to another table that has a date in one column and supplemental data in another. Here is how I am getting all the dates within a range:
select trunc(sysdate-90) + rownum -1 from all_objects where rownum <=90
The problem is joining this to another table on date. If I run:
select trunc(sysdate-90) + rownum -1, t.col2 from all_objects
left join (select date, col2 from table) t on trunc(sysdate-90) + rownum -1 = t.date
where rownum <=90
Then it only displays the first record from t.col2 for all values within the date range. How do I properly join these two tables?
Thanks
A better way to get the previous 90 days is to use the dual / connect by trick:
select trunc(sysdate-level) as the_date from dual connect by level <= 90
Now, you should be able to do something like:
select the_date, t.col2
from (select trunc(sysdate-level) as the_date from dual connect by level <= 90) date_tbl
left join t on date_tbl.the_date = t.date
Don't use all_objects, this is a really bad idea in terms of performance and your DBA will want to string you up. I have no idea where this idea of using all_objects came from originally, but I wish it would die!
In order to get a list of dates, you can implement a much simpler and fast bit of SQL:
SELECT TRUNC( SYSDATE ) - ROWNUM a_date
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
If you want an offset, you can add that
SELECT TRUNC( SYSDATE ) - ROWNUM + (the offset) a_date
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
If you want a different order you can wrap if in another select and then order:
SELECT a_date
FROM (
SELECT TRUNC( SYSDATE ) - ROWNUM + (the offset)
FROM DUAL
CONNECT BY ROWNUM <= (the number of days you want)
)
ORDER BY a_date ASC
You can then embed this into your existing statement, bearing in mind that you need it in a sub-select. The reason for that is so that your ROWNUM values don't get mixed up:
SELECT t.date, t.col_2
FROM ( SELECT TRUNC( SYSDATE ) - ROWNUM a_date
FROM DUAL
CONNECT BY ROWNUM <= 90 ) date_list
LEFT JOIN table t ON date_list.a_date = t.date
ORDER BY a_date DESC
I have a table with a column "Date". The date will be displayed in a calendar in a cyclic form. For example the records date will be shown in the calendar in a certain day each week till a specific date (let's say TerminationDate). To summarize in my table I have the Date and the TerminationDate columns like this:
Table:
Title | Date | TerminationDate
------------------------------
t1 | d1 | td1
and I want to achieve something like this:
From query:
Title | Date | TerminationDate
------------------------------
t1 | d1+7 | td1
t1 | d1+14| td1
t1 | d1+21| td1
.................... till Date < TerminationDate
Does anyone have any idea how to achieve this in Oracle?
This should do the trick
select distinct title, date + ( level * 7 ), termination_date
from table
connect by date + ( level * 7 ) < termination_date
EDIT:
Forget about above query, since the rows must be connected only with itself there has to be
connect_by prior title = title
but that means a loop must be created. Unfortunately Oracle connect by clause throws an error if there is a loop whatsoever. Even if you use
date + ( level * 7 ) < termination_date
Oracle still stops execution immediately where it detects a loop at runtime. Using nocycle returns the result, but that returns only the first record which is date + 7
ANSWER:
So i had to approach to the problem in a different way
select t.*, date + (r * 7) as the_date
from table t,
(select rownum as r
from dual
connect by level < (select max(weeks) --max date interval as weeks to be used to repeat each row as much, if you know the max weeks difference you can use the constant number instead of this sub-query
from (select ceil((termination_date - date) / 7) as weeks
from table ))
)
where r < ceil((termination_date - date) / 7)
Let me know is there is any conufsion or performance problem
I have not tested the query ,but it should work like as you need
SELECT t1, d1 + (7 * LEVEL), termination_date
FROM tab
WHERE d1 + (7 * LEVEL) < termination_date
CONNECT BY LEVEL <= CEIL( (termination_date - d1) / 7);
EDIT
SELECT DISTINCT t1,dt,termination_date
FROM(
SELECT t1, d1 + (7 * LEVEL) dt, termination_date
FROM tab
WHERE d1 + (7 * LEVEL) < termination_date
CONNECT BY LEVEL <= CEIL( (termination_date - d1) / 7)
);
Here's one more way to do it
SELECT
*,
date + ( ROWNUM * 7 ) as modified_date
FROM (
SELECT
title,
date,
termination_date
FROM
table
) WHERE date + ( ROWNUM * 7 ) < termination_date