Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Ravi.

Asked: March 05, 2002 - 9:59 pm UTC

Last updated: July 11, 2005 - 8:34 am UTC

Version: 8.1.5

Viewed 1000+ times

You Asked

Hello Tom ,
I am trying to retrieve the dates between two given dates , though this is possible by writing a pl/sql Block , I would like to achieve the same in a Single Sql Statement , if possible .
Doing it this way ...
declare
ed number;
bd date;
begin
select sysdate-30,sysdate-(sysdate-30) into bd,ed from dual;
for i in 0..ed loop
dbms_output.put_line(bd+i);
end loop;
end;
/
-------------------------------------------------------------
Also I would like to know if u are in the process or have already written a Book like Expert One-on-One for Oracle 9i . I would like to buy the latest edition , so the question .

Thanx and Regards
R.Venkatesh


and Tom said...

select sysdate-30+(rownum-1)
from all_objects
where rownum <= 30
/


Expert One on One is the current book. I have no current plans to do a 9i release of that (frankly, 90% or more of the material in there applies to 9i or even 7.3 as well).

I've been toying with the idea of a performance book but haven't signed on the dotted lines yet.

Rating

  (15 ratings)

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

Comments

Retrieve Dates Between Two Dates

Bharath, March 06, 2002 - 12:31 pm UTC

How about this...

SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) <= TO_DATE('01/31/2002','MM/DD/YYYY')
;

Tom Kyte
March 06, 2002 - 12:58 pm UTC

if you factor that predicate so that ROWNUM is on the LHS by itself -- it'll be OK

rownum <= TO_DATE('01/31/2002','MM/DD/YYYY')-TO_DATE('01/01/2001','MM/DD/YYYY')+1

otherwise, it'll evaluate that for every row in all_objects -- not just the 31 you want.

SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) <=
TO_DATE('01/31/2002','MM/DD/YYYY')

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.02 0.03 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 28 0.74 0.72 0 14080 4 396
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 30 0.76 0.75 0 14080 4 396


SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
FROM ALL_OBJECTS
WHERE
rownum <=
TO_DATE('01/31/2002','MM/DD/YYYY') - TO_DATE('01/01/2001','MM/DD/YYYY') + 1


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.02 0.02 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 28 0.10 0.08 0 1636 4 396
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 30 0.12 0.10 0 1636 4 396


Dates b/w two given dates ....

Priya, March 07, 2002 - 12:27 am UTC

Tom,
This is fine as long as the number of rows in DBA_objects or for that matter any other table or view may have sufficient rows as as to return the dates . In the case where the dates b/w 2 given dates may exceed the number of rows in for eg : DBA_OBJECTS , we have a problem . Do we have a fix for this ?

Tom Kyte
March 07, 2002 - 7:58 am UTC

if you need more then 2 to 3 years of dates generated -- (given that all objects in 73 is around 1000 rows and 20-30,000 for a java install in 8i), I question what you are doing. What would you do with 1,000 days in a list?

The solution of course would be for YOUR application to install and populate a table that is big enough for you. Part of your install would be:

create table rows ( x int );
begin
for i in 1 .. 1000000 -- or whatever
loop
insert into rows values ( i );
end loop;
commit;
end;
/

In 9i, we can use a pipelined PLSQL function to generate as many rows as you like on the fly. See
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:2270447621346 <code>
for an example of that.

Securtiy Implications

A reader, March 07, 2002 - 2:43 am UTC

Yesterday Eveing had a Long Argument with my friend when i told him this solution he was saying that a DBA in a Production environment will not give Rights to all_objects.

I Felt it was a Bit Crazy because AFAIK i know we wont have any security Implication on Giving access to all_objects.

What are your thoughts on this.

Thanks.

Best Regards,
Ganesh R

Tom Kyte
March 07, 2002 - 8:15 am UTC

A DBA in production doesn't have to give the rights -- select on all_objects is always granted to PUBLIC upon install. Many Many products will not work without it. It is called "the data dictionary".

This is in catalog.sql:

grant select on ALL_OBJECTS to PUBLIC with grant option
/

all of the ALL_* and USER_* views are selectable by PUBLIC (anyone with CREATE SESSION can see them)

Hopefully your friend is not a DBA for a production environment!

Generating Dates...

A reader, March 09, 2002 - 1:04 am UTC

Hi Tom,

Thanks for the earlier Response. I have another one for you. [This actuall was the Main thread of the previous discussion that i had posted]

I Usually use this Generate Dates for a varitey of purposes.

Select '01-jan-2001' + rownum from all_objects where rownum < 100;

The Above Query Generates 100 Dates..

Now i heard from people that instead of using the all_objects view u should create a Table that has 4000 Odd rows that we can use so that if all_objects Privs have been revoked then also u'r function will work.

IMHO ... I feel there is no penality in using all_objects for this purpose.

Your thoughts on these...

Regards,
Ganesh R

Tom Kyte
March 10, 2002 - 5:25 pm UTC

Read the above comments about all_objects and security.

If you every have a database where all_objects access has been revoked, you are in a database that is

o not supported by Oracle support in all likelyhood.
o won't support most any 3rd party application

ALL_OBJECTS is *always* granted select on to PUBLIC. If it isn't, the database is broken and needs to be fixed.

The very idea that accessing ALL_OBJECTS is a security "risk" is utter nonsense. I would be very interested in hearing from others who think otherwise AND WHY -- so I can correct their thinking.



Question

Florin, March 31, 2004 - 3:55 am UTC

Hi Tom,

I'd like to retrieve a list of dates between 2 given dates.
Ex:
Input: 20040331 and 20040325
Output:
20040325
20040326
20040327
20040328
20040329
20040330
20040331

Many thanks in advance,


Tom Kyte
March 31, 2004 - 8:43 am UTC

ok, I'd use BETWEEN then?

... .where column between :a and :b

Thanks

Florin, March 31, 2004 - 10:01 am UTC

Hi,

I don't have a table containing dates.
I'd like to retrieve a list of dates by giving 2 dates as an input (using dual or something like this).

Tom Kyte
March 31, 2004 - 10:38 am UTC

oh, you mean like the original answer above??

select to_date(your_start)+rownum-1
from all_objects
where rownum <= to_date(your_end)-to_date(your_start)+1

Brilliant !!!

Florin, March 31, 2004 - 11:09 am UTC

Thanks a lot!!!

Amazing, but...

Joe Khoobyar, June 14, 2005 - 4:28 pm UTC

Utterly amazing. I used to just do this in a stored procedure, now I'll use this instead. One issue w/ it though:

Let's say I have a rather complex query that joins to an inline view of the dates returned by the original answer to the original question: (select sysdate-30+(rownum-1) from all_objects where rownum <= 30 ... or some derivate thereof)

I'd like to explain plan (or just set autotrace on), but the system won't let me do so (it gives an ORA-01039). I'm assuming this is because the all_objects table obviously isn't in my schema, and I don't have permissions to run explain plan on it...

Is there a workaround for this? Or do have revert back to using the stored proc if I want to explain the query plan?



Tom Kyte
June 14, 2005 - 4:41 pm UTC

in 9ir2 and up, use

with as_many_rows_as_you_need
as
(select level l
from dual
connect by level <= :N )
select .....
from as_many_rows_as_you_need, .....

Generating Dummy Rows

A reader, June 14, 2005 - 6:39 pm UTC

Here is another technique I learnt on Asktom for generating rows from dual instead of all_objects

SQL>  select rownum from (select null from dual group by cube(1,1));         

    ROWNUM
----------
         1
         2
         3
         4

SQL>  select rownum from (select null from dual group by cube(1,1,1)) ;

    ROWNUM
----------
         1
         2
         3
         4
         5
         6
         7
         8

SQL> l
  1   select rownum from (select null from dual group by cube(1,1,1))
  2* where rownum < 6
SQL> /

    ROWNUM
----------
         1
         2
         3
         4
         5

cube(1,1,1,1) for 16 or less
cube(1,1,1,1,1) for 32 or less 
and so on
 

Alex, June 30, 2005 - 4:21 pm UTC

Hi Tom,

I was wondering the best way to write a query to select rows only where the date in my in coming parameter is 55 days apart from the column i'm checking. Right now I'm checking if they're equal.

So if I'm doing something like this:

cursor c1 is
select due_date
from t
where due_date = pdue_date;

I would like to change it to return only rows 55 days before or after the incoming date. I'm sure this is very simple but my brain is spent from a tough week.
I'm using 8.1.7. Thanks a lot.

Tom Kyte
June 30, 2005 - 4:48 pm UTC

isn't that just:

where due_date between pdue_date-55 and pdue_date+55;



Alex, July 05, 2005 - 3:04 pm UTC

Yeah that's all it was, thanks. I have another trickier problem now I'm hoping you can help with. This is also a cursor where I'm looking to see if my parameter date falls between two date columns, start_date and end_date. If it does not, check between possible_start and possible_end.

The problem is that the date columns can be null. So I think what's happening is I'm getting results back where these columns are null even though they don't fall in between my date criteria because if something's null, how can a date be between null and some date, right?

I thought about using nvl, but then I realized I would have to provide a bogus date to return because they are date columns. Thus influencing my results. My code looks like this:

cursor c1 is
select date_oid
from schedules
where pdate between start_date and end_date
or pdate between possible_start and possible_end;

So let's say my parameter is 03/07/2004. I would get two
rows back:

START_DAT END_DATE POSSIBLE_ POSSIBLE_
--------- --------- --------- ---------
28-JUN-05 15-NOV-03 15-NOV-06
07-MAR-04 08-MAR-04 07-MAR-04 08-MAR-04

Do you have any advice? Is my problem clear?

Tom Kyte
July 05, 2005 - 3:17 pm UTC

sorry, clear as mud -- no example to work with.

do you want start_date of null to be like "negative infinity"?? not sure why you put null in there if so.

Alex, July 05, 2005 - 3:32 pm UTC

Ok fair enough. Let me try something, isn't there a way to order nulls so they show up and the end of a result set?
I'm thinking maybe if I order my date columns in that manner
the row I'm looking for would be the first returned.
In other words flip flop the two rows I posted.

Tom Kyte
July 05, 2005 - 3:51 pm UTC

order by column NULLS FIRST;

order by column NULLS LAST;

Santosh Upadhyay, July 06, 2005 - 8:41 am UTC

Hi Tom,
The entire thread was very useful to me in writing a query. For the given data:
EMpno num1 num2
123 2004 2008
456 2005 2007
I need something like:
EMpno num1 num2
123 2004 2005
123 2005 2006
....
123 2007 2008
456 2005 2006
456 2006 2007

I tried many things but still missing out few rows. Need some expert help.
Thanks in advance.

Santosh.

Tom Kyte
July 06, 2005 - 9:00 am UTC

umm, I don't even know what I'm looking at here.

This is a little vague, don't you think?

A stab...

Philip Moore, July 06, 2005 - 10:17 am UTC

Hi Tom,
With your approval - I would like to take a stab at this teaser :)

I believe that what the submitter was requesting was to list single-year intervals one at a time for a given employee (although he is using NUMBER datatypes as a date (year) - a Tom Kyte no-no).

Here goes:

philip@ORA9iR2 -> DROP TABLE tester
2 /

Table dropped.

philip@ORA9iR2 ->
philip@ORA9iR2 -> CREATE TABLE tester
2 ( empno INT
3 , num1 INT
4 , num2 INT
5 , CONSTRAINT tester_pk PRIMARY KEY (empno)
6 )
7 /

Table created.

philip@ORA9iR2 ->
philip@ORA9iR2 -> INSERT INTO tester (empno, num1, num2)
2 VALUES (123, 2004, 2008)
3 /

1 row created.

philip@ORA9iR2 ->
philip@ORA9iR2 -> INSERT INTO tester (empno, num1, num2)
2 VALUES (456, 2005, 2007)
3 /

1 row created.

philip@ORA9iR2 ->
philip@ORA9iR2 -> SELECT empno
2 , num1 + (lvl - 1) AS num1
3 , num1 + lvl AS num2
4 FROM tester
5 , (SELECT LEVEL AS lvl
6 FROM dual
7 CONNECT BY LEVEL < 9999
8 ) cart
9 WHERE cart.lvl <= (tester.num2 - tester.num1)
10 ORDER BY tester.empno
11 , cart.lvl
12 /

EMPNO NUM1 NUM2
---------- ---------- ----------
123 2004 2005
123 2005 2006
123 2006 2007
123 2007 2008
456 2005 2006
456 2006 2007

6 rows selected.

--
Hope this was helpful...

Thanks.

Sincerely,

Philip Moore

A reader, July 11, 2005 - 3:38 am UTC

APologies for psoting the question not so clear. I tried the solution suggested by Philip but I am getting the error ORA-01436. SHould there be a 'start with'?

Many thanks,
Santosh.

Tom Kyte
July 11, 2005 - 8:34 am UTC

the dual trick to generate rows only works in software written this century, eg: 9ir2 and above.

More to Explore

DBMS_OUTPUT

More on PL/SQL routine DBMS_OUTPUT here