ORA-01846: not a valid day of the week in procedure - sql

Hello In my oracle sql procedure , I am trying to get first sunday of selected year with code below
select next_day(to_date('01.01.'||v_year,'DD.MM.YYYY'), 'Sunday') into v_py from dual;
v_year is number format like 2020 and v_py is date format
but this give me error like title.
When I wrote this query like below then I can see the result. Why isn't it working in procedure
select next_day(to_date('01.01.'||2020,'DD.MM.YYYY'), 'Sunday') from dual;
Thanks in advance

Your code is valid. However, the second argument to next_day() is language-dependent, so I suspect that your database is not English.
You can check the language of the database with the following query:
select * from v$nls_parameters where parameter = 'NLS_LANGUAGE';
From there on, you can change the second parameter from 'Sunday' to the corresponding day name in the database language.
It is also possible to build a language-indenendent expression, taking advantage of the fact that to_char() supports passing a language as last argument (unlike nextday()):
select trunc(to_date(v_year,'YYYY'), 'YYYY')
+ 7
- to_char(trunc(to_date(v_year,'YYYY'), 'YYYY'), 'D', 'NLS_DATE_LANGUAGE=ENGLISH')
into v_py from dual;
trunc(to_date(v_year,'YYYY'), 'YYYY') is just another way to get the first day of the year from the given parameter.

Function NEXT_DAY() depends on NLS_DATE_LANGUAGE, TRUNC(..., 'D') depends on NLS_TERRITORY, so either are not suitable for an NLS independent solution.
Use TRUNC(..., 'IW') which is based on ISO-8601 where begin of the week is always defined on Monday. Would be this:
v_py := TRUNC(TO_DATE('01.01.'||v_year, 'DD.MM.YYYY')+7, 'IW') - 1;
There is no need for SELECT ... INTO ... FROM dual

Related

SQL* Is there a way to make a procedure that will verify what time and day?(Beginner) SQL*

It's for a school project and I know it may seems basic but I'm wondering how to make a procedure that will verify what time and day it's actually is like monday and not the date. thank you
Please use this -
select to_char(sysdate, 'Day') from dual;
Pls note, you can use 'DAY' to get all caps, 'Day' to get initcaps. There are whole lot of things you can do in terms of formatting the date. You can refer to the link below.
https://livesql.oracle.com/apex/livesql/file/content_GCEY1DN2CN5HZCUQFHVUYQD3G.html
I would recommend using 'Dy' and using 3-character abbreviations for the date:
select to_char(sysddate, 'Dy')
The reason is that if you use 'Day' it pads the name with spaces. So, you end up with seeming absurdities such as this:
select (case when to_char(date '2000-01-01', 'Day') = 'Saturday' then 1 else 0 end),
to_char(date '2000-01-01', 'Day')
from dual;
I should also note that to_char() takes a third parameter for the language so you don't have to use English names.

Oracle SQL Sysdate not comparing properly with nls_date_format='DD-MON-RR'

I have a oracle sql code that is getting an inconsistent result when using sysdate compared to the manually entered dates (for last thursdays or fridays).
Ex:
Alter session set nls_date_format='DD-MON-RR';
query with the following where clause that runs custom dates on Mondays:
Manual entry that works correctly:
WHERE DATECOLUMN= to_date('2021/01/29','yyyy/mm/dd') -- key in previous Friday's date
WHERE DATECOLUMN= case when to_char(sysdate,'D')=2 then sysdate-3
else to_date('9999/01/01','yyyy/mm/dd')
end -- if today is monday, then pick last Friday's date for the run else pick a dummy date.
I am certain the alter session script in python is working correctly since I tested in Alteryx as well. However, I am still having trouble getting the result to match for manually entered dates. I tested the comparison of sysdate-3 vs. DATECOLUMN using select and it seems to be working on TOAD so I am not seeing the issue causing a mismatch.
What could be the cause of such a mismatch in results when both pieces of code are supposed to compare the DATECOLUMN with the same value?
The result of to_char(sysdate,'D') depends on current user sessions NLS_TERRITORY settings.
For more reliable solution better use for example:
TO_CHAR(sysdate, 'fmDay', 'NLS_DATE_LANGUAGE = American') = 'Monday'
SYSDATE - 3 returns both DATE and TIME components, e.g.
SQL> select sysdate - 3 from dual;
SYSDATE-3
-------------------
05.02.2021 11:06:04
SQL>
Your code suggests that DATECOLUMN contains only DATE component. Therefore, try with
trunc(sysdate) - 3
which will "remove" time component (actually, will set value to previous midnight):
SQL> select trunc(sysdate) - 3 from dual;
TRUNC(SYSDATE)-3
-------------------
05.02.2021 00:00:00
So, your code would then be
WHERE DATECOLUMN= case when to_char(sysdate,'D')=2 then trunc(sysdate) - 3
else to_date('9999/01/01','yyyy/mm/dd')
end

Oracle query concatenate date

I have the following query:
select *
from mytable
where to_char(mydate,'mm/dd/yyyy') between ('05/23/2013')
and ('06/22/2013')
I need to change it to make dynamically so that I won't modify it every month from 05/23/2013 to 06/23/2013 for example:
('05/23/' + (select to_char(sysdate, 'yyyy') from dual))
but this is giving an error. Any suggestions?
What I need to do: Every month I need to run this query to get the records between 23rd of this month and 23rd of the last month.
Oracle uses || as the concatenation operator:
('05/23/' || (select to_char(sysdate, 'yyyy') from dual))
BTW, David is right. If you really want to compare string representations of dates (but why?), use a date format that is ordered the same way as dates:
to_char(mydate,'yyyy/mm/dd')
You're performing a comparison on strings, not on dates, so your code doesn't work the way you think it does.
Based on the string logic, "05/23/2000" is between "05/22/2013" and "06/24/2000".
Keep the data types as date and Oracle will get the comparison right.
Possibly what you want is:
select *
from my_table
where mydate >= add_months(trunc(sysdate,'MM'),-1)+22 and
mydate < trunc(sysdate,'MM')+22
but it's difficult to tell without a description of what the requirement actually is.
How about this one?
SELECT '(''05/23/'''||to_char(sysdate, 'yyyy')||'''' FROM DUAL
Have not testet because I have no Oracle database right now, needs checking for quote escapes...
Do you need exact days from the month? You can also substract days from sysdate:
SELECT (sysdate - 30) FROM DUAL
You may also use concat function
concat('05/23/', (select to_char(sysdate, 'yyyy') from dual))

In oracle SQL, how would you obtain the timestamp representing the start of the week?

I'm using an Oracle 9i database and want to obtain, within a function, the timestamp representing the start of the week, i.e. The most recent monday, at 00:00:00.
I am aware that the timestamp representing the start of the current day is TO_TIMESTAMP(SYSDATE).
You can use the function next_day to get that:
SQL> select next_day(sysdate-7, 'MONDAY') FROM DUAL;
NEXT_DAY
---------
29-APR-13
Getting the start of the week should work with trunc (see docs).
So,
select to_timestamp(trunc(sysdate, 'D')) from dual
should work.
However, depending on your NLS settings, the first day of the week for oracle may well be Sunday.
this appears to return Monday before the day of week in question at midnight. to prove out just play around with sysdate and subtract days...
select case when to_Char(sysdate,'d') = 1 then
trunc(sysdate-6)
else
trunc(sysdate - (to_Char(sysdate,'d')-2))
END
from dual;
You can truncate a date to Monday with:
select trunc(sysdate, 'IW') FROM DUAL;

Oracle to_date function with quarter-format

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)
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.
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.
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.)
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
To calculate in Oracle the first day of a quarter and the last day of a quarter from the year and quarter:
I Use the fact
start_month= -2 + 3 * quarter
last_month = 3 * quarter
variable v_year number
variable v_quarter number
exec :v_year :=2017
exec :v_quarter:=4
select :v_year as year,
:v_quarter as quarter,
to_date(:v_year||to_char(-2+3*:v_quarter,'fm00'),'yyyymm') as quarter_start,
last_day(to_date(:v_year||to_char(3*:v_quarter,'fm00')||'01 23:59:59','yyyymmdd hh24:mi:ss')) as quarter_end
from dual a;
YEAR|QUARTER|QUARTER_START |QUARTER_END
2017| 4|2017-10-01 00:00:00|2017-12-31 23:59:59