views:

56

answers:

4

I need to find some records created in a range of quarters. For example, I'm looking for all records created between the 4th quarter of 2008 and the 1st quarter of 2010. I have this in my WHERE-clause:

...and r.record_create_date between to_date('2008 4','YYYY Q')
                                and to_date('2010 1','YYYY Q')

but Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened. Is this even a valid way to find values in between calender quarters, or is there a better way?


Also interesting, and possibly related, if I execute this:

select to_date('2009','YYYY') from dual;

The value displayed in my IDE is 2009-08-01. I would have expected 2009-08-04, since today is 2010-08-04.

This:

select to_date('2009 1','YYYY Q') from dual;

of course, fails.

(Oracle 10g)

+1  A: 

Someone asked the same question on OTN: http://forums.oracle.com/forums/thread.jspa?threadID=1081398&tstart=255

The crux of the issue is that you can not specify "Q" in the TO_DATE function.

Given that you're already specifying a portion of the date, why not provide the entire date? Mind too that to_date('2010 1','YYYY Q') would give you Jan 1st, 2010 when you really want March 31st, 2010... at a second to midnight.

OMG Ponies
when you really want March 31st, 2010 *at 11:59:59*
Shannon Severance
@Shannon Severance: Of all the people to take the bait... :)
OMG Ponies
A: 

I think the best way is to just input the quarter start date and quarter end dates without even bothering with to_date. I think if you use

between '1-Jan-10' and '31-Dec-10'

for example, then you don't (in Oracle I believe) need to_date and it isn't much more difficult than typing in the quarter number

jle
Well, for my final code I had plans to loop through quarter numbers 1 to 4, but I guess I can have an array indexed 1 to 4 with the appropriate values for the actual start and end dates of the quarters (or something like that).
FrustratedWithFormsDesigner
+2  A: 

Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened.

See the second column of table 2.15 at http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34948. Not all format elements are allowed when converting to dates, timestamps, etc.

I recommend against using between for date range checks. People often will miss values within the ending day that the expect to be included. So I would translate:

and r.record_create_date between to_date('2008 4','YYYY Q')
                             and to_date('2010 1','YYYY Q')

To

and to_date('2008-10-01', 'YYYY-MM-DD') <= r.record_create_date 
and record_create_date < to_date('2010-04-01', 'YYYY-MM-DD') -- < beginning of 2Q2010.
Shannon Severance
+1  A: 

Since the relationship between quarters to months is one-to-many, it doesn't make sense to do TO_DATE('2008 1', 'yyyy q'); what date should be returned? The first of the quarter, the end of the quarter, ...? (On the other hand, converting a date to a quarter - like TO_CHAR(SYSDATE, 'yyyy q') makes sense because a specific date only exists in one quarter.)

So, if you do want a query that looks for a date that falls between two quarters, you will have to "rolll your own" (explicitly stating the dates of the start/end of a quarter.)

As a side note, in case anyone is considering not using TO_DATE please do not use things like: WHERE date_value BETWEEN 'date string1' and 'date string2' without the TO_DATE function. It assumes a default date format and under certain situations can avoid potentially useful indexes altogether.

Below is one example where the same query can have a different result.

select sysdate from dual where sysdate between '1-Jan-10' and '31-Dec-10';


SYSDATE
---------
04-AUG-10

SQL> alter session set nls_date_format = 'YYYY-MM-DD';

Session altered.

SQL> select * from dual where sysdate between '1-Jan-10' and '31-Dec-10'; 

no rows selected

(Notice that in the second instance no error is returned. It just assumes Jan 10, 0001 and Dec. 10th, 0031.)

Patrick Marchand