SQL to Return Results from Week 13 Year 2008 (not grouped)
-
10-07-2019 - |
Question
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
Solution
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
OTHER TIPS
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.
I prefer to create a table for weeks for each year that matches my domains local understanding, but thats just me.