Issue when comparing result of to_char(myDate, 'DAY') to a string - sql

I have been trying to find what the issue might be but I am just out of luck and don't understand this problem at all.I have the following code:
CREATE OR REPLACE FUNCTION ckeckDay(dateC in date)
RETURN VARCHAR
IS
day VARCHAR(15);
checkFriday VARCHAR(1);
BEGIN
checkFriday := 'N';
day := to_char(dateC, 'DAY');
IF day = 'FRIDAY' THEN
checkFriday := 'Y';
END IF;
RETURN day;
END;
/
the dateC is set to Friday (even tested it by returning day instead of the day variable and it returns Friday.) However the IF statement never evaluates to true even though the day variable is indeed Friday.Any ideas how to go around this issue.Thanks

If you want to be really robust about this then you ought to force the NLS setting to English and apply the "fill mode" format model FM for trimming leading and trailing spaces.
If To_Char(DateC,'fmDAY', 'nls_date_language=english') = 'FRIDAY'
Then ...

It is because day variable contains a blank padded value. Use trim function to get rid of leading and trailing spaces:
IF trim(day) = 'FRIDAY' THEN
checkFriday := 'Y';
END IF;
And please use VARCHAR2 datatype for string variables. Do not use VARCHAR.

Related

How to check is it parameters in declare section equal to sysdate - stored procedure

I have a problem with checking variables, when I try to convert those variables in date to check if statement, when creating stored procedure.
Code like this
create or replace procedure test
as
dan nvarchar2(2) :='1';
mesec nvarchar2(2) := '1';
godina nvarchar2(4) := '2016';
begin
if to_date(dan||mesec||godina,'dd-mon-yy') != sysdate
then
for dan in 1..12 loop
dbms_output.put_line(dan);
end loop;
end if;
end;
These 3 variables are nvarchar2, what is needed by to_date function as a parameter. But when try to convert these variables to a date, and check is it equal to sysdate, give me next error
ORA-01861: literal does not match format string
I don't know how to fix it?
It's going to be a rather long comment so I put it in the answer.
Now, let's start from the beginning. Why did you have that
ORA-01861: literal does not match format string
in the first place.
Your final, representing date, string literal you had after concatenating different parts of the date looked like this
112016
Date format string (dd-mon-yy) used in the to_date() function expects day and month to be represented by two digits: day 01 and month 01 not 1 and 1 as is in your case. So for the to_date() function to be executed successfully it should've looked like this
to_date('01012016', 'dd-mm-yy')
NOT like this(your case)
to_date('112016', 'dd-mm-yy')
Then you would have to take time portion of the date into consideration when comparing those two dates - you would have to get rid of it by applying TRUNC() function to the sysdate (after that, time defaults to midnight, exactly what you need if you want to compare just two dates).
The condition becomes
to_date('01012016', 'dd-mm-yyyy') != trunc(sysdate)
If you wanted to go another way and convert sysdate to string and compare two string literals you would also need either work with two digits day and month (01 and 01 respectively) or leave the as they were (each one of them is represented by one digit) and use FM format modifier in the format string to get rid of leading zeros. So it would look either like this
declare
dan nvarchar2(2) :='01';
mesec nvarchar2(2) := '01';
godina nvarchar2(4) := '2016';
begin
if godina||mesec||dan != to_char(sysdate, 'yyyymmdd');
...
end;
OR
declare
dan nvarchar2(2) :='1';
mesec nvarchar2(2) := '1';
godina nvarchar2(4) := '2016';
begin
if godina||mesec||dan != to_char(sysdate, 'fmyyyymmdd');
...
end;
Here the correct way:
create or replace procedure test
as
dan nvarchar2(2) :='1';
mesec nvarchar2(2) := '1';
godina nvarchar2(4) := '2016';
begin
if godina||mesec||dan != to_char(sysdate,'yyyymmdd')
then
for dan in 1..12 loop
dbms_output.put_line(dan);
end loop;
end if;
end;

Retirement Date Function in Oracle SQL

Please I want to calculate the retirement of an employee from his Date of First Appointment using a function before retrieving the result using a trigger and store it in the Retirement date column: but I am not good with the syntax please can someone help:... Below is my code for the function
CREATE OR REPLACE Function EDOR_DATE
(DOFA IN date)
RETURN date
IS
NEW_EDOR_RESULT date;
BEGIN
SELECT DOFA + ((365*35) + 9) as NEW_EDOR
FROM EMPLOYEES_MASTER_DETAILS;
fetch NEW_EDOR into NEW_EDOR_RESULT;
RETURN NEW_EDOR_RESULT;
END;
First a couple comments on the changes you indicated you made to the function:
Removing the "IN" from the declaration actually accomplishes nothing.
If you do not specify "IN', "OUT", or "IN OUT" the compiler defaults to "IN". All removing it does is a change from an explicit to implicit declaration.
Placing a semi-colon (;) where indicated will generate an error.
As for the function itself you are making it way more complicated than necessary. A simple assignment is all that's needed. Also, Boneist's suggestion of add_months is the correct function for adding years as it will adjust for leap year and number of days per month (if needed).
Thus your function reduces to:
create or replace function edor_date(dofa in date)
return date
is
l_edor_date date;
begin
l_edor_date := add_months(dofa, 35*12) ;
return l_edor_date ;
end;
or even further to just:
create or replace function edor_date (dofa in date)
return date
is
begin
return add_months(dofa, 35*12) ;
end;
BTW: I actually like the idea of using a function for this as it hides the implementation details of the business rule. However, it does impose a slight overhead for each call.
CREATE OR REPLACE Function EDOR_DATE
(DOFA date)
RETURN date
IS
NEW_EDOR_RESULT date;
BEGIN
--I am converting this to a case statement that is easier to read
select
case
when dofa - dob <= 25 then dofa + ((365*35)+9)
else dob + ((365*60)+9)
end
into new_edor_result
from employees_master_details;
return new_edor_result;
end;
This can be done without needing to use a function. Also, why are you adding numbers of days? A year is not 365 days long. You should be using the add_months() feature.
Here is how I would do this, assuming all the columns in question belong to the EMPLOYEES_MASTER_DETAILS table:
select emp_id,
dob,
hire_date,
case when months_between(hire_date, dob) <= 25 * 12 then add_months(hire_date, 35*12)
else add_months(dob, 60*12)
end retirement_date
from EMPLOYEES_MASTER_DETAILS emd;
If you need to calculate the retirement upon insert, then simply use the case expression as part of the insert statement.

Oracle Date Format with input Parametres - PLSQL

I'm getting an error as follows:
ORA-01858 - a non-numeric character was found where a numeric character was expected.
Although it isn't really accurate, I believe it's related to the date formatting in the following lines.
ELSIF par_report_eff_date_start IS NOT NULL AND par_report_eff_date_start = to_date(par_report_eff_date_start, 'fxdd-mm-yyyy')
and
ELSIF par_report_eff_date_end IS NOT NULL AND par_report_eff_date_end = to_date( par_report_eff_date_end ,'fxDD-MM-YYYY')
I'm trying to get the parameters to render date in the format of 'dd/mm/yyyy' as it is passed in, but i'm not sure how to get around this.
I have looked, but have limited web access at work, so I can't use the regular sites.
procedure collect_mon_comm_bal_data_part (
par_report_eff_date_start DATE DEFAULT NULL, --dd/mm/yyyy;
par_report_eff_date_end DATE DEFAULT NULL) --dd/mm/yyyy;
is
v_report_eff_date_start DATE;
v_report_eff_date_end DATE;
BEGIN
IF par_report_eff_date_start IS NULL
THEN
-- Oracle job runs at the beginning of each month
select trunc(trunc(sysdate,'Mon')-1,'Mon')
into v_report_eff_date_start
from dual; -- Start of month Var
ELSIF par_report_eff_date_start IS NOT NULL AND par_report_eff_date_start = to_char(par_report_eff_date_start, 'fxdd/mm/yyyy')
THEN
v_report_eff_date_start := par_report_eff_date_start;
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_start || 'Is The Start Date');
ELSE
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_start || 'Is is the wrong format, needs tp be in dd/mm/yyyy');
GOTO the_end;
END IF;
IF par_report_eff_date_end IS NULL
THEN
-- Oracle job runs at the beginning of each month
select trunc(sysdate,'MM')-1
into v_report_eff_date_end
from dual; -- Start of month Var
ELSIF par_report_eff_date_end IS NOT NULL AND par_report_eff_date_end = to_char(par_report_eff_date_end, 'fxdd/mm/yyyy')
THEN
v_report_eff_date_end := par_report_eff_date_end;
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_end || 'Is The Start Date');
ELSE
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_end || 'Is is the wrong format, needs tp be in dd/mm/yyyy');
GOTO the_end;
END IF;
END;
You don't want to convert a date to a date. You want to convert it to a character string. So try:
to_char(par_report_eff_date_start, 'fxdd-mm-yyyy')
I'm not sure what the rest of the logic is supposed to be doing. But, you use to_date() to convert a value to a date and to_char() to convert a value to a string.
I notice that you have parameter par_report_eff_date_start, and a variable v_report_eff_date_start. You probably want to compare those in the following line:
ELSIF par_report_eff_date_start IS NOT NULL AND par_report_eff_date_start = to_char(par_report_eff_date_start, 'fxdd/mm/yyyy')
But now you are comparing the date parameter with a text value derived from the parameter value. I don't think that's what you want.
A date is just a date, and you only have to think about format masks when converting it to another data type (like VARCHAR2).
So why not use:
ELSIF par_report_eff_date_start IS NOT NULL AND trunc(par_report_eff_date_start) = trunc(sysdate, 'Mon')
I've added trunc() to the parameter value, and because you want the first day of the month (I guess!!), trunc(sysdate, 'Mon') will do just fine.
Thank You Gordon, I think I've found a way around it.
procedure collect_mon_comm_bal_data_part (
par_report_eff_date_start VARCHAR2 DEFAULT NULL, --dd/mm/yyyy;
par_report_eff_date_end VARCHAR2 DEFAULT NULL) --dd/mm/yyyy; is
v_report_eff_date_start DATE; v_report_eff_date_end DATE;
BEGIN
IF par_report_eff_date_start IS NULL
THEN
-- Oracle job runs at the beginning of each month
select trunc(trunc(sysdate,'Mon')-1,'Mon')
into v_report_eff_date_start
from dual; -- Start of month Var
ELSIF par_report_eff_date_start IS NOT NULL AND to_date(par_report_eff_date_start, 'dd/mm/yyyy') = to_date(par_report_eff_date_start,'fxdd/mm/yyyy')
THEN
v_report_eff_date_start := to_date(par_report_eff_date_start, 'dd/mm/yyyy');
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_start || 'Is The Start Date');
ELSE
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_start || 'Is is the wrong format, needs tp be in dd/mm/yyyy');
GOTO the_end; END IF;
IF par_report_eff_date_end IS NULL
THEN
-- Oracle job runs at the beginning of each month
select trunc(sysdate,'MM')-1
into v_report_eff_date_end
from dual; -- Start of month Var
ELSIF par_report_eff_date_end IS NOT NULL AND to_date(par_report_eff_date_end, 'dd/mm/yyyy') = to_date(par_report_eff_date_end,'fxdd/mm/yyyy')
THEN
v_report_eff_date_end := to_date(par_report_eff_date_end, 'dd/mm/yyyy');
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_end || 'Is The Start Date');
ELSE
DBMS_OUTPUT.PUT_LINE(par_report_eff_date_end || 'Is is the wrong format, needs tp be in dd/mm/yyyy');
GOTO the_end; END IF;
So I want it to default to a monthly run, for the previous month if the parameters are null. And I want it to run for selected dates, should they be put in manually at execution, this way I can run it for a year period to build up a proper sample of data, that will be updated as it changes going forward.
I have tried to pass in bogus dates, and null dates and they hit the exception or run for a month of data as expected, so this works for me.
Thanks again guys, I really the help!

PLS-00103 in a Trigger

CREATE OR REPLACE TRIGGER comprobarHora
BEFORE INSERT
ON PEDIDOS
FOR EACH ROW
declare
v_data int(4);
v_dia varchar(20);
begin
select to_char(sysdate,'hh24mi') into v_data from dual;
select to_char(sysdate, 'day') into v_dia from dual;
if ((v_data BETWEEN (2031, 0830)) OR (v_dia='saturday') OR (v_dia='sunday')) then
raise_application_error(-20001,' No hi ha estoc');
end if;
end;
Can someone help me with this,error information is about line 8.
/
The immediate problem is on line 8 of the PL/SQL section (not the entire statement), which is:
if ((v_data BETWEEN (2031, 0830)) OR (v_dia='saturday') OR (v_dia='sunday')) then
That isn't how the BETWEEN condition is specified, it should be:
if v_data BETWEEN 2031 and 0830 OR v_dia='saturday' OR v_dia='sunday' then
But that still won't work as the lower value has to come first ("If expr3 < expr2, then the interval is empty"). You aren't handling the time going past midnight anyway. You could either test:
if v_data >= 2031 OR v_data <= 0830 OR v_dia='saturday' OR v_dia='sunday' then
or swap the BETWEEN conditions and negate it:
if NOT (v_data BETWEEN 0830 and 2031) OR v_dia='saturday' OR v_dia='sunday' then
As Gordon Lindoff pointed out, you're storing a string in a numeric field, so you're doing an implicit conversion. If you want to keep the 'time' as a number you should convert it explicitly really. You should probably also be making sure you get the day name back in the language you expect, using the third parameter to to_char; and the day name is padded by default, so using the abbreviation is simpler.
CREATE OR REPLACE TRIGGER comprobarHora
BEFORE INSERT
ON PEDIDOS
FOR EACH ROW
declare
v_data int(4);
v_dia varchar(20);
begin
v_data := to_number(to_char(sysdate,'hh24mi'));
v_dia := to_char(sysdate, 'dy', 'NLS_DATE_LANGUAGE=ENGLISH');
if v_data >= 2031 OR v_data <= 0830 OR v_dia='sat' OR v_dia='sun' then
raise_application_error(-20001,' No hi ha estoc');
end if;
end;
/

trying to concat a couple of TO_DATE(SUBSTR) to get a combined date of 'dd-mon-yyyy' from strings..?

I previously ask about converting strings to dates and formatting them..
split string based on character position in ORACLE 11g SQL
Here is the solution I have come up with where WKENDING is VARCHAR2 and RYEAR is date
The WKENDING has data that looks like '523'(mmdd) and RYEAR is '2012'..
UPDATE OB_SEL_LST84_AGG_WKEND SET WKENDYEAR = (TO_DATE((TO_DATE(substr(WKENDING,3,2)),'dd')||(TO_DATE(substr(WKENDING,0,1)),'mon')||(TO_DATE(TO_CHAR(RYEAR)),'yyyy')),'dd-mon-yyyy');
I am now getting an error 'ORA-00907: missing right parenthesis', I've double checked the parenthesis a couple of times and they look right to me.. any help would be great.. thanks!
UPDATE
- After looking at the syntax of what I have above I thought that maybe there are too many TO_DATE attempted conversions going on. So, I shortened it to this..
UPDATE OB_SEL_LST84_AGG_WKEND SET WKENDYEAR = (TO_DATE((substr(WKENDING,3,2))||(substr(WKENDING,0,1))||TO_CHAR(RYEAR)),'dd-mon-yyyy');
I'm still getting the missing parenthesis error though.. ARGH!
Since the data type of the wkendyear column is DATE, you should just need to
UPDATE OB_SEL_LST84_AGG_WKEND
SET wkendyear = to_date( lpad(WKENDING,4,'0') || RYEAR, 'mmddyyyy' )
This assumes, of course, that all your string data can be converted into a valid date. As soon as you have a wkending of 0229 and a ryear of 2013 (or some other combination of strings that are not a valid date), the TO_DATE function is going to throw an exception. That's one of the reasons that storing dates in VARCHAR2 columns is generally problematic.
If not all of your data can be converted correctly to a DATE, you can create a function that attempts to do the conversion and returns a NULL if there is an exception. For example
CREATE OR REPLACE FUNCTION my_to_date( p_str IN VARCHAR2, p_format IN VARCHAR2 )
RETURN DATE
IS
l_dt DATE;
BEGIN
l_dt := to_date( p_str, p_format );
RETURN l_dt;
EXCEPTION
WHEN others THEN
RETURN NULL;
END;
Your UPDATE statement would then become
UPDATE OB_SEL_LST84_AGG_WKEND
SET wkendyear = my_to_date( lpad(WKENDING,4,'0') || RYEAR, 'mmddyyyy' )
You can also look for rows where the string cannot be converted to a date
SELECT *
FROM OB_SEL_LST84_AGG_WKEND
WHERE my_to_date( lpad(WKENDING,4,'0') || RYEAR, 'mmddyyyy' ) IS NULL
AND (wkending IS NOT NULL or ryear IS NOT NULL)