ansi SQL date function in Oracle - sql

I realize I could use a combination of to_char and to_date and what not as a work-around, but I'm trying to figure out why this doesn't work. I am using Oracle 12.1
select '2016-10-01'
from dual
union all
select to_char(2016)||'-10-01'
from dual;
Each side of the union produces identical output: 2016-10-01. If I then try to use the ANSI date syntax (as described here: http://blog.tanelpoder.com/2012/12/29/a-tip-for-lazy-oracle-users-type-less-with-ansi-date-and-timestamp-sql-syntax/ ), it only works on the first one, not the second one:
select date '2016-10-01'
from dual
This returns: 10/1/2016
But if I try this:
select date to_char(2016)||'-10-01'
from dual;
I get on:
ORA_00936 missing expression error.
I can code a work-around, but I'm stumped as to why one works and the other does not.

It won't work that way because DATE is not a function but a literal.
The terms literal and constant value are synonymous and refer to a fixed data value.
Date Literals
You can specify a DATE value as a string literal, or you can convert a character or numeric value to a date value with the TO_DATE function. DATE literals are the only case in which Oracle Database accepts a TO_DATE expression in place of a string literal.
You could use TO_DATE function.
select TO_DATE(to_char(2016)||'-10-01', 'YYYY-MM-DD')
from dual;
Rextester Demo
EDIT:
Using dynamic-SQL:
DECLARE
i DATE;
stmt VARCHAR2(100);
BEGIN
stmt := q'{SELECT DATE '}' || TO_CHAR(2016) || '-01-01' || q'{' FROM dual}';
EXECUTE IMMEDIATE stmt INTO i;
DBMS_OUTPUT.PUT_LINE('i =>' || i);
END;

Related

Receiving ORA-01843: not a valid month error while retrieving data between two dates

I have a column in oracle database table which is Varchar2. In this column I am storing date line 29-1-2021 or 28-12-2020. I want to retrieve data from below query between two dates then I am getting error of Invalid month. How can I resolve this issue ?
SELECT Line_Stop_Id, Function_name, Product_family, line_description, Reason_Category, Reason_detail,
Product_item, product_description, request_raised_date, request_raised_time, cm.EMP_NAME as raised_by, Lse.User_Closer_Description,
Cm1.Emp_Name as Closer_User, Lse.User_Closer_Date, Lse.Final_Closer_Description, Lse.Final_Closer_Date, Lse.Final_Closer_Time,
Cm2.Emp_Name as Final_Closer, Lse.Resource_Effected ,
ROUND(24*(sysdate - to_date(Request_Raised_Date
||' '
||request_raised_time, 'DD-Mon-RR HH24:MI:SS'))) AS TAT
FROM Xx_Lsp_Linestoppage_Entry lse
Left join Emp_Master cm ON Lse.Raised_By = Cm.Emp_No
Left join Emp_Master cm1 ON Lse.Closer_User = Cm1.Emp_No
Left join Emp_Master cm2 ON Lse.Final_Closed_By = Cm2.Emp_No
where TO_DATE(Lse.Request_Raised_Date, 'DD-Mon-RR') Between TO_DATE('01-Jan-21', 'DD-Mon-RR') and TO_DATE('29-Jan-21', 'DD-Mon-RR');
You should check the validity of your date values stored as strings before a conversion and do a cleanup of them (of fix them).
This can be done via, for example, cursor in PL/SQL block (or wrapped in function to export query results or filter by its value):
declare
l_date date;
begin
for r in (
select distinct
request_raised_time
from Xx_Lsp_Linestoppage_Entry
/*To reduce rows in cursor*/
where not regexp_like('^\d{2}-\d{2}-\d{4}')
) loop
begin
l_date := to_date(r.q, 'dd-mm-yyyy');
exception
when others then dbms_output.put_line('Invalid date: ' || r.q);
end;
end loop;
end;
/
db<>fiddle here
Note, that you need to quote dashes inside format to make it exact, because Oracle treats unquoted dash as any symbol from quite wide set of delimiters. So it will process 01/12/2020 as date '2020-12-01', not as invalid date.
select to_date('01/12/2021', 'dd-mm-yyyy') as dt from dual;
DT
--------------------
2021-12-01T00:00:00Z
Elapsed: 00:00:00.002
1 rows selected.
select to_date('01-12-2021', 'dd-mm-yyyy') as dt from dual;
DT
--------------------
2021-12-01T00:00:00Z
Elapsed: 00:00:00.002
1 rows selected.
select to_date('01$12$2021', 'dd-mm-yyyy') as dt from dual;
DT
--------------------
2021-12-01T00:00:00Z
Elapsed: 00:00:00.002
1 rows selected.
You are storing the date in the format 29-1-2020 (dd-mm-yyyy) so use this format in the to_Date
replace
DD-Mon-RR
with
dd-mm-yyyy
Probably being the Lse.Request_Raised_Date field of type varchar2 it contains more information than you need for your query.
I think the best way is to select a string that only identifies the part necessary for the identification of the date using the function:
substr(string , string start from , string lenght )
This is an example:
select to_date(substr('29-01-2021 23:01:55',1,10),'DD-MM-YYYY') from dual;
TO_DATE (SU
----------
29-01-2021
instead of my string '29 -01-2021 23:01:55 ' enter field Lse.Request_Raised_Date
You can use on conversion error:
to_date(Request_Raised_Date || ' ' || request_raised_time
default null on conversion error, 'DD-Mon-YYYY HH24:MI:SS')
This returns NULL instead of an error.
Hopefully, this is a lesson on why you should not store date/time values as strings. You can actually look for the bad values using:
select Request_Raised_Date, request_raised_time
from Xx_Lsp_Linestoppage_Entry
where to_date(Request_Raised_Date || ' ' || request_raised_time
default null on conversion error, 'DD-Mon-YYYY HH24:MI:SS') is null and
Request_Raised_Date is not null
This may give you some ideas on how to fix the data.

Covert NVARCHAR to number (digits after decimal sign vary)

Do you know how I can convert NVARCHAR in Oracle if the digits after decimal signs vary?
I need this in order to convert it to date, using this formula:
select to_date('12/30/1899', 'MM/DD/YYYY') + 40676.2641666667
from dual;
The thing is that the digits in the second part (40676.2641666667) vary so I can use to_number(t.column, '99999D9999999999'), because it doesn't work this way.
Do you have any suggestions?
These all work for me:
select (date '1899-12-30') + '40676.2641666667'
from dual
and:
select (date '1899-12-30') + cast('40676.2641666667' as number)
from dual
and:
select (date '1899-12-30') + 40676.2641666667
from dual
One caveat is that internationalization settings might require comma instead of period.
Oracle doesn't have NVARCHAR, it has NVARCHAR2. The following works for me:
DECLARE
nv_Number NVARCHAR2(0032) := 40676.2641666667;
dt_Base DATE := TO_DATE('12/30/1899', 'MM/DD/YYYY');
dt_Result DATE;
BEGIN
dt_Result := dt_Base + TO_NUMBER(nv_Number);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(dt_Result,'yyyymmdd hh24:mi:ss'));
END;
Result: 20110513 06:20:24

Oracle sql - convert string to date

i am having problems with converting a varchar(yyyymmdd) to date(yyyymmdd).
i have a procedure with a parameter (pdate varchar2, yyyymmdd format) which needed to be converted to date type in yyyymmdd format as well.
so far i tried.
vdate date;
vdate := (to_date(SUBSTR(pdate,1,4)+SUBSTR(pdate,5,2)+SUBSTR(pdate,7,2), 'yyyymmdd'));
this threw a error of ORA-01840: input value not long enough for date format.
any help would be appreciated.
Just use a to_date
select to_date(MyDate, 'yyyymmdd')
from mytable
Test with:
select to_date('20170831','yyyymmdd')
from dual
Also, to concatenate in Oracle, use a double pipe ||
select 'Chicken'||'Food'
from dual
If pdate has other characters after yyyymmdd, and yyyymmdd is in the beginning of the whole text, you can just use
SELECT TO_DATE(SUBSTR(pdate,1,8), 'yyyymmdd')
FROM yourtable;
Example
SELECT TO_DATE(SUBSTR('20170831 10:30am',1,8), 'yyyymmdd')
FROM dual;
Otherwise, you can directly use TO_DATE() as suggested by most that replied

Oracle - literal does not match format string

I have a function which takes 2 date parameters:
CREATE OR REPLACE FUNCTION date_equal
(
date1 IN DATE,
date2 IN DATE
)
RETURN NUMBER IS
equal BOOLEAN;
BEGIN
equal := NVL(date1, '1999-01-01') = NVL(date2, '1999-01-01');
IF equal THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END date_equal;
/
Now, when I run select on the table which provides data for the function it runs okay:
SELECT TO_DATE(some_date, 'YYYY-MM-DD') FROM tbl
But when I try to use that in function call it fails:
SELECT date_equal(TO_DATE(some_date, 'YYYY-MM-DD'), TO_DATE(some_date, 'YYYY-MM-DD')) FROM tbl
The error message is "literal does not match format string". Does anyone know why would that happen?
When you do
NVL(date1, '1999-01-01')
Oracle tries to convert '1999-01-01' to a date implicitly (since date1 is a date).
For doing this it uses NLS_DATE_FORMAT which may not be yyyy-mm-dd
You can use explicit converting:
NVL(date1, to_date('1999-01-01', 'yyyy-mm-dd'))
or use the ANSI way
NVL(date1, date '1999-01-01')
The error message is most certainly caused by the pieace reading NVL(date1, '1999-01-01').
Try nvl(date1, date '1999-01-01') instead.

Oracle/SQL: invalid number format model when concatenating a date and a time into a single date value

I am trying to create a trigger in Oracle that compares two dates and then deletes records if the difference of the two dates falls below a certain value. I have a full date value with the format 'DD-MON-YYYY HH24MI' and then for the second date value, I want to concatenate a 'DD-MON-YYYY' value with a 'HH24MI' value.
The problem I am getting is that when I try to concat the date and time value together using to_char, and then using to_date on that returned value, it gives me a ORA-01481 invalid number format error. The relevant lines from the trigger itself are below. If anyone can help me out with this, it would be greatly appreciated!
CREATE OR REPLACE TRIGGER dateTrig
...
DECLARE
day date;
ftime date;
CURSOR c_table1 IS SELECT ...;
BEGIN
FOR entry IN c_table1 LOOP
day := to_date(entry.fdate);
ftime := to_date(to_char(day, 'DD-MON-YYYY') || ' ' || to_char(entry.dtime, 'HH24MI'), 'DD-MON-YYYY HH24MI'); -- this is the line that is causing the error
dbms_output.put_line(day || ', ' || ftime);
END LOOP;
END;
/
Since DTIME is not a date type, you cannot use TO_CHAR with a date format. If it's zero padded to a length of 4 characters, you can simplify it like this:
ftime := to_date(to_char(day, 'DD-MON-YYYY') || ' ' || entry.dtime, 'DD-MON-YYYY HH24MI');
I think your best to avoid using to_char's if you can and try to stick with functions that return values in their native date formats, this way you may also not need to run a to_date command on your string, you may have some success with the following or a close relation to it:
ftime := Trunc(Sysdate) || ' ' || Extract(Hour From entry.dtime) || ':' Extract(Minute From entry.dtime)
Best of luck