Skip to Main Content
  • Questions
  • How to show missing date between given range

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, zam.

Asked: November 16, 2000 - 3:58 am UTC

Last updated: August 08, 2006 - 8:03 am UTC

Version: 7.3 or 8.0.5

Viewed 1000+ times

You Asked

Hi
How to show missing date from given date range except Friday
Suppose I have some columns like this

Empno attend_date
------ --------------
101 5-jan-2000
101 9-jan-2000
101 10-jan-2000
101 11-jan-2000
101 12-jan-2000
101 13-jan-2000
101 16-jan-2000

I wanna see missing date from 5-jan-2000 to 17-jan-2000 except Friday the result must be
Like this
Empno attend_date
--------- --------------
101 6-jan-2000
101 8-jan-2000
101 15-jan-2000
and so onÂ…..

thank u
regards,
zam


and Tom said...

Yes, we can do that. You'll need to tweak this for performance and for your needs. I've made it totally generic. If you know the date ranges -- it can go faster. If you do this for one employee at a time -- it can go faster. I've set it up to dynamically discover the date range and be applied to all employees. I'll build the example bit by bit:

drop table t2;
create table t2 ( r primary key ) as select rownum r from all_objects where rownum < 500;

we need a table with lots of rows with just a number. We need this to synthesize the "missing" rows


here is your table, i've put 2 emps in there...
drop table t;
create table t ( empno int, attend_date date );

insert into t values ( 101, '5-jan-2000' );
insert into t values ( 101, '9-jan-2000' );
insert into t values ( 101, '10-jan-2000' );
insert into t values ( 101, '11-jan-2000' );
insert into t values ( 101, '12-jan-2000' );
insert into t values ( 101, '13-jan-2000' );
insert into t values ( 101, '16-jan-2000' );

insert into t values ( 102, '6-jan-2000' );
insert into t values ( 102, '9-jan-2000' );
insert into t values ( 102, '10-jan-2000' );
insert into t values ( 102, '11-jan-2000' );
insert into t values ( 102, '12-jan-2000' );
insert into t values ( 102, '13-jan-2000' );
insert into t values ( 102, '16-jan-2000' );

First, we figure out how to get the date ranges by employee. What I need to know for an emp is the start date of the range and the number of days... You might supply these as BIND VARIABLES instead of a query as I am:


select empno, min(attend_date) start_date,
max(attend_date)-min(attend_date)+1 num_days
from t
group by empno
/

EMPNO START_DAT NUM_DAYS
---------- --------- ----------
101 05-JAN-00 12
102 06-JAN-00 11



Now I make up all of the dates in that range for example employee:

select empno, start_date+r-1
from t2,
( select empno, min(attend_date) start_date,
max(attend_date)-min(attend_date)+1 num_days
from t
group by empno ) t1
where t2.r <= t1.num_days
/

EMPNO START_DAT
---------- ---------
101 05-JAN-00
101 06-JAN-00
101 07-JAN-00
101 08-JAN-00
101 09-JAN-00
101 10-JAN-00
101 11-JAN-00
101 12-JAN-00
101 13-JAN-00
101 14-JAN-00
101 15-JAN-00
101 16-JAN-00
102 06-JAN-00
102 07-JAN-00
102 08-JAN-00
102 09-JAN-00
102 10-JAN-00
102 11-JAN-00
102 12-JAN-00
102 13-JAN-00
102 14-JAN-00
102 15-JAN-00
102 16-JAN-00

23 rows selected.

Now, I just need to outer join that query with the original table. the rows we want to keep are rows such that the original table had no "mate" and the date is not friday:

select *
from ( select empno, start_date+r-1 attend_date
from t2,
( select empno, min(attend_date) start_date,
max(attend_date)-min(attend_date)+1 num_days
from t
group by empno ) t1
where t2.r <= t1.num_days ) A,
t B
where a.empno = b.empno(+)
and a.attend_date = b.attend_date (+)
and b.attend_date is NULL
and to_char( a.attend_date, 'dy' ) <> 'fri'
/


EMPNO ATTEND_DA EMPNO ATTEND_DA
---------- --------- ---------- ---------
101 06-JAN-00
101 08-JAN-00
101 15-JAN-00
102 08-JAN-00
102 15-JAN-00


And that is it...




Rating

  (8 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

unbelievable

Umesh, February 13, 2003 - 5:16 am UTC


What if...?

Tom, June 03, 2003 - 2:03 pm UTC

What if you wanted the dates that were missed to be aggregated into ranges? For instance, say an employee missed May 2, 3, 4, 5 and May 18,19,20. How would you write a query to return:

May 2, May 5
May 18, May 20

?


Tom Kyte
June 03, 2003 - 2:08 pm UTC

ops$tkyte@ORA817DEV> select empno, last_attend_date, attend_date
  2    from (
  3  select empno, attend_date,
  4         lag(attend_date) over (partition by empno order by attend_date) last_attend_date
  5    from t
  6         )
  7   where attend_date <> last_attend_date+1
  8  /

     EMPNO LAST_ATTEND ATTEND_DATE
---------- ----------- -----------
       101 05-JAN-2000 09-JAN-2000
       101 13-JAN-2000 16-JAN-2000
       102 06-JAN-2000 09-JAN-2000
       102 13-JAN-2000 16-JAN-2000


 

MISSING DATES

A reader, June 04, 2003 - 3:40 am UTC

Good Example

have a dought

Pasko, August 07, 2006 - 4:27 am UTC

Hi Tom,

i wanted to use this Example as a basis for another Query,but i have a Question:

i note that the first row from the inner Query:

select empno, attend_date,
lag(attend_date) over (partition by empno order by attend_date) last_attend_date
from t ,

has last_attend_date = null , but this row is not returned
by the outer Query when we apply the condition:

'where attend_date <> last_attend_date+1'

I know everything compared to null is unknown, but should we explicitly handle for this in the above Query may be with nvl?

Thanks in advance,

Pasko


Tom Kyte
August 07, 2006 - 8:08 am UTC

where (attend_date <> last_attend_date+1 or last_attend_date is null)

have a dought

Pasko, August 07, 2006 - 5:06 am UTC

Hi Tom,

Sorry, please ignre the above follow-up from Pasko.
i would like to re-phrase the Question:

Suppose i would like list only the missing Dates without considering the empno using the Analytic Functions approach:

select last_attend_date, attend_date, row_num
  from (select  attend_date,
    lag (attend_date) over (order by attend_date ) last_attend_date,
row_number()  over ( order by attend_date) row_num
from t  )
 where attend_date <> last_attend_date + 1
 order by 1

SQL> select last_attend_date, attend_date, row_num
  2    from (select  attend_date,
  3                 lag (attend_date) over (order by attend_date ) last_attend_date,
  4                        row_number()  over ( order by attend_date) row_num
  5            from t
  6               )
  7   where attend_date <> last_attend_date + 1
  8   order by 1
  9  /

=>>Results:

LAST_ATTEND ATTEND_DATE    ROW_NUM
----------- ----------- ----------
06 JAN 2000 09 JAN 2000          3
09 JAN 2000 09 JAN 2000          4
10 JAN 2000 10 JAN 2000          6
11 JAN 2000 11 JAN 2000          8
12 JAN 2000 12 JAN 2000         10
13 JAN 2000 13 JAN 2000         12
13 JAN 2000 16 JAN 2000         13
16 JAN 2000 16 JAN 2000         14

At least i didn't expect this Result Set for example for Rows with row_num: 4,6,8,10,12,14

because i asked only for a condition 
where 'attend_date <> last_attend_date + 1'

Thanks in advance,

Pasko
 

Tom Kyte
August 07, 2006 - 8:10 am UTC

why did you not expect this result?

i didn't see the Duplicates

Pasko, August 07, 2006 - 9:52 am UTC

Hi Tom,

Thanks for your Response.
Sorry again, the Data appears to have duplicated attend_date's if the empno is not considered, so in that case the Result is correct and expected.

I was actually trying to get the all Missing attend_dates( as asked by the original Poster) by using analytic functions.

Tom Kyte
August 07, 2006 - 9:35 pm UTC

did you mean to PARTION BY the empno?

without partition by empno

Pasko, August 08, 2006 - 7:52 am UTC

Hi Tom,

i wanted to get the missing attend_dates without using the
Partition by empno.

Tom Kyte
August 08, 2006 - 8:03 am UTC

Now I have no clue what you are looking for.

If you do not partition by empno, you get the same attend date over and over (it is right there in the data, it is not duplicated - it just exists that way)

did you mean to DISTINCT the set of attend_dates before processing the data?

select <.... analytics whatever here >
from (select distinct attend_date from t)


Thanks Tom

pasko, August 08, 2006 - 12:28 pm UTC

Hi Tom,

Thanks for your response.
I have already found a solution to my problem using the Examples in this thread.

and yes, i had to use 'distinct' first in order to remove the Duplicates.