views:

315

answers:

3

Hi folks,

I'm trying to use a Year-Week format in oracle SQL to return results only from a range of Year-Weeks.

Here's what I'm trying

SELECT * FROM widsys.train trn WHERE trn.WID_DATE>=TO_DATE('2008-13', 'YYYY-IW') AND trn.WID_DATE<=TO_DATE('2008-15', 'YYYY-IW') ORDER BY trn.wid_date

but it shoots this error.

ORA-01820: format code cannot appear in date input format but fails on ORA

Any suggestions on what I can use?

Thanks kindly,

Thomas

A: 

How about

select * from widsys.train trn 
  where to_char(trn.wid_date, 'YYYY-IW') =  ?
  order by trn.wid_date

and for ranges

select * from widsys.train trn 
  where to_char(trn.wid_date, 'YYYY-IW') between ? and ?
  order by trn.wid_date

The range will use string comparisons, which works fine if smaller numbers are zero-padded: "2009-08" and not "2009-8". But the 'IW' format does this padding.

Thilo
Thanks for the answer! It worked fine, but returned odd results when I tried to extend it to a range. I guess >= or <= don't make sense in comparing strings like "2009-13"?
Ranges should work. Just make sure that you have padding with zero: '2009-08' and not '2009-8'.
Thilo
+5  A: 

You could flip it around and do a string compare.

 SELECT * 
 FROM widsys.train trn 
 WHERE to_char(trn.WID_DATE, 'YYYY-IW') ='2008-13'
 ORDER BY trn.wid_date;

I suppose it makes sense that to_date() doesn't work with IW, as the start of the week is somewhat ambiguous - some locales start the week on Sunday, others Monday, etc. Generating a truncated week of the year, unlike a truncated day, month, or year, would therefore be difficult.

edit:

I agree that the natural sort should suffice, but you got me thinking. How would you compare a given date and a formatted YYYY-IW string? I took a stab at it. This attempt could be fashioned into a function that takes a date and a YYYY-IW formatted varchar, but you would need to replace the hard coded strings and the to_date() function calls, and perform some clean up.
It returns a -1 if the passed in date is before the year/weekofyear, 0 if the date falls within the specified weekofyear, and 1 if it is after. It works on ISO week of year, as does the 'IW' format token.

 select (case 
      when input.day < a.startofweek then -1
      when input.day < a.startofweek+7 then 0
      else 1 end)
 from 
 (select 
 -- //first get the iso offset for jan 1, this could be removed if you didn't want iso 
    (select (max(to_number(to_char(to_date('2008','YYYY') + level,'DDD')))) 
     from dual 
     where to_number(to_char(to_date('2008','YYYY')  + level,'IW')) 
      <2 connect by level <= 6) -6
    +
 -- //next get the days in the year to the month in question   
    (select ((to_number(substr('2008-13', 6,2))-1)*7) from dual) startofweek 
     from dual) a, 
 -- //this is generating a test date
  (select to_number(to_char(to_date('2008-07-19', 'YYYYMMDD'), 'DDD')) day 
    from dual) input, 
  dual
akf
That works spot on! Thanks. But I also need to turn it into a week range now (I've edited my question). I've tried >='2008-13' but I don't think this is understood for a string.
Try it, the natural ordering for strings should allow you to do things like '2008-13' > x > '2008-18'
Dana the Sane
A: 

I prefer to create a table for weeks for each year that matches my domains local understanding, but thats just me.

Mark Schultheiss
this table needs updating on an annual basis, no?
akf
Yes, but you could do that easily using automated mechanics, or just put in 100 years worth - the performance difference for 520 rows vs 52 is trivial. It has the added advantage in that if you use Client defined weeks, you now have a mechanisim to do that, just add a client id to the table for each client.
Mark Schultheiss