Oracle NVL function not allows second parameter as datetime - oracle

select nvl(trunc(null),trunc(sysdate)) from dual;
While executing above query i am getting following error
ORA-00932: inconsistent datatypes: expected NUMBER got DATE
look like when i take string or number instead of trunc(sysdate) it run fine.

From here:
The first parameter to NVL is determining the expected datatype of the
returned column, with the trunc function defaulting that to NUMBER
because it's parameter is NULL. The second parameter to NVL needs to
match that datatype which it doesn't because it is a date.
SQL> select nvl(trunc(sysdate), sysdate) as mydate from dual;
MYDATE
-------------------
26/05/2006 00:00:00
SQL> select nvl(trunc(null), sysdate) as mydate from dual;
select nvl(trunc(null), sysdate) as mydate from dual
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected NUMBER got DATE
or you can do like this:
SELECT NVL(TO_DATE(TRUNC(NULL)),SYSDATE) FROM dual;

The second parameter must be of the same datatype as the first parameter. The trunc function is overloaded. Apparently when you pass it a null, it interprets the value as a number and returns a number. Any of the following works:
SELECT NVL (NULL, TRUNC (SYSDATE)) FROM DUAL;
SELECT NVL (TRUNC (TO_DATE (NULL)), TRUNC (SYSDATE)) FROM DUAL;
SELECT NVL (TRUNC (CAST (NULL AS DATE)), TRUNC (SYSDATE)) FROM DUAL;
Follow up:
If the variable that is being used is properly defined as a date, then you should not have a problem. As an example, the following runs without error:
DECLARE
FUNCTION f
RETURN DATE IS
BEGIN
RETURN NULL;
END f;
BEGIN
DBMS_OUTPUT.put_line (NVL (TRUNC (f), TRUNC (SYSDATE)));
END;
In order to get an answer that actually solves your problem, I would recommend editing the question to include code that actually demonstrates the problem.

Related

PL-SQL block the result of the first query as a parameter for the second query

How can i get the result of the first query as a parameter for the second query.
the goal is to get the smaller date and apply it to the second query.
SELECT
MAX(datе)
FROM
datе_table
WHERE
datе <= TO_DATE('2022-07-31', 'YYYY-MM-DD')
select name
from table_name
where datе = MAX(datе)
he result of the first query must be applied in the where clause of the second query
Instead of just max(date), use the whole 1st query:
select name
from table_name
where date = (select max(date)
from date_table
where date <= date '2022-07-31'
)
(of course, column name can't be just date; that's reserved for datatype name)
If you plan to re-use maximum date in PL/SQL code, then store it into a (local) variable, e.g.
declare
l_max_date date;
l_fun1_retval number;
l_fun2_retval varchar2(20);
l_val number;
begin
select max(date) into l_max_date
from date_table
where date <= date '2022-07-31';
l_fun1_retval := function1 (l_max_date);
l_fun2_retval := function2 (l_max_date);
select count(*) into l_val
from table_3
where date_value = l_max_date;
end;

How to find record which throws an error in a SELECT (Oracle)

I have a problematic SQL Sentence, when it runs
SELECT code, my_date, my_time
FROM my_table
WHERE to_date(to_char(my_date, 'YYYY-MM-DD') || ' ' || my_time, 'YYYY-MM-DD HH24:MI:SS') > sysdate - 5
I always get: ORA-01841: (full) year must be between -4713 and +9999, and not be 0
This is the definition of my table:
CREATE TABLE my_table (code NUMBER(10), my_date DATE, my_time VARCHAR2(8));
ALTER TABLE my_table ADD CONSTRAINT pk_my_table PRIMARY KEY (code);
CREATE INDEX i_my_table_001 ON my_table (my_date);
But If I add an extra restriction, I never get the error:
SELECT code, my_date, my_time
FROM my_table
WHERE my_date > trunc(sysdate - 5)
AND to_date(to_char(my_date, 'YYYY-MM-DD') || ' ' || my_time, 'YYYY-MM-DD HH24:MI:SS') > sysdate - 5
All validations are ok. Not invalid dates, not invalid times
Is there any way to find which is the 'offending' record?
Brute force is not an option because table has more than 25M records.
If you are on Oracle 12.2 or higher, you can use the VALIDATE_CONVERSION function to see which rows could be giving you an error.
Example Query
WITH
my_table (code, my_date, my_time)
AS
(SELECT 1, date '2020-01-01', '08:00:00' FROM DUAL
UNION ALL
SELECT 2, date '2020-01-31', '25:00:00' FROM DUAL)
SELECT *
FROM my_table
WHERE validate_conversion (to_char(my_date, 'YYYY-MM-DD') || ' ' || my_time AS DATE, 'YYYY-MM-DD HH24:MI:SS') = 0;
Result
CODE MY_DATE MY_TIME
_______ ______________ ___________
2 2020-01-31 25:00:00
Update
One update that may be a bit easier than cloning your database and upgrading is to create your own function to validate the date and call that from a test SQL statement like the on below. I'm not sure if defining a function within a common table expression is supported in 12.1, but if it is not, you can make it a standalone function and call that from your query.
WITH
FUNCTION validate_date (p_date DATE, p_time VARCHAR2)
RETURN NUMBER
AS
l_test_date DATE;
BEGIN
l_test_date :=
TO_DATE (TO_CHAR (p_date, 'YYYY-MM-DD') || ' ' || p_time, 'YYYY-MM-DD HH24:MI:SS');
RETURN 1;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
SELECT *
FROM (SELECT 1 AS code, DATE '2020-01-01' AS my_date, '08:00:00' AS my_time FROM DUAL
UNION ALL
SELECT 2, DATE '2020-01-31', '25:00:00' FROM DUAL) my_table
WHERE validate_date (my_date, my_time) = 0;

How to form oracle function - TIMESTAMP input -Date Math - Number Return

I am trying to create an oracle function that takes a TIMESTAMP input, calculates number of days between that timestamp and today, and returns that number. I am not constructing it properly, and not sure how to fix:
create or replace FUNCTION "DAYSSINCEOPENDATE" (OPENDATE IN TIMESTAMP)
RETURN NUMBER
AS
retval NUMBER;
BEGIN
SELECT (SYSDATE - CAST(OPENDATE AS DATE) FROM DUAL)) into retval;
return retval;
END;
Any help is appreciated. Thanks!
As #Aleksej said in a comment, the order of your clauses is incorrect; you need
SELECT SYSDATE - CAST(OPENDATE AS DATE) into retval FROM DUAL;
So:
create or replace FUNCTION "DAYSSINCEOPENDATE" (OPENDATE IN TIMESTAMP)
RETURN NUMBER
AS
retval NUMBER;
BEGIN
SELECT SYSDATE - CAST(OPENDATE AS DATE) into retval FROM DUAL;
return retval;
END;
/
select DAYSSINCEOPENDATE(timestamp '2018-01-01 12:13:14.5') from dual;
DAYSSINCEOPENDATE(TIMESTAMP'2018-01-0112:13:14.5')
--------------------------------------------------
204.773368
You don't actually need the retval variable or the query against dual though; you can simplify that to
create or replace FUNCTION "DAYSSINCEOPENDATE" (OPENDATE IN TIMESTAMP)
RETURN NUMBER
AS
BEGIN
return SYSDATE - CAST(OPENDATE AS DATE);
END;
/
select DAYSSINCEOPENDATE(timestamp '2018-01-01 12:13:14.5') from dual;
DAYSSINCEOPENDATE(TIMESTAMP'2018-01-0112:13:14.5')
--------------------------------------------------
204.773368

REGEXP_LIKE for anything not like MM/DD/YYYY

What is the regular expression to be used to catch anything in the database which is not of the expression MM/DD/YYYY.
(I want everything which is apart from the above mentioned format, could be dd-mon-yy or yyyy/mm/dd etc)
I am using the below regexp query
select birth_date FROM table_name where not regexp_like (birth_date, '[0-9][0-9]/[0-9][0-9]/[0-9]{4}');
Firstly, a suggestion to you , don't use a VARCHAR2 / CHAR type for DATEs in database.
You may create a function using TO_DATE
CREATE OR REPLACE FUNCTION validmmddyyyy (p_str IN VARCHAR2)
RETURN NUMBER
AS
V_date DATE;
BEGIN
V_Date := TO_DATE (p_str, 'MM/DD/YYYY');
RETURN 1;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
select validmmddyyyy('09/12/2018') from DUAL;
1
select validmmddyyyy('13/12/2018') from DUAL;
0
select validmmddyyyy('2018/12/01') from DUAL;
0
Use your query like,
select birth_date FROM table_name where validmmddyyyy(birth_date) = 0
If you are lucky enough to use Oracle 12c R2, you could make use of DEFAULT..ON..CONVERSION ERROR clause of TO_DATE
SELECT *
FROM table_name
WHERE TO_DATE (birth_date default null on conversion error,'MM/DD/YYYY') IS NULL
You can use the below query to get other than the required format :
select * from dual where not regexp_like('2013/24/feb','[0-9]{2}.[[:alpha:]]{3}.[0-9]{4}')

Insert function parameter in select part of sql query

Oracle PL/SQL function that takes a string as a parameter.
What is the correct syntax for including this parameter in the Select portion of a statement.
The function is :
create or replace function GetHorizonTime(HorizonWindowMinutes IN VARCHAR2)
return timestamp
IS
RETVALUE TIMESTAMP;
BEGIN
SELECT SYSDATE + Interval '''||HorizonWindowMinutes||''' MINUTE INTO RETVALUE
FROM DUAL;
RETURN RETVALUE;
END;
/
SELECT GetHorizonTime('720') FROM DUAL;
The select statement should translate to:
SELECT SYSDATE + Interval '720' MINUTE INTO RETVALUE
FROM DUAL;
create or replace function GetHorizonTime(HorizonWindowMinutes IN NUMBER)
return timestamp
IS
RETVALUE TIMESTAMP;
BEGIN
RETURN (SYSDATE + HorizonWindowMinutes/1440);
END;
/
Use the numToDSInterval function rather than constructing an interval literal
create or replace function GetHorizonTime(HorizonWindowMinutes IN NUMBER)
return timestamp
IS
BEGIN
return SYSDATE + numtodsinterval( HorizonWindowMinutes, 'minute' );
END;
/
If you're working with timestamp data types, I would generally add an interval to the current_timestamp or localtimestamp rather than to a date like sysdate. If you don't need fractional seconds or time zones, on the other hand, it would seem sufficient to just return a date rather than a timestamp.
Another way of doing it is this one:
SELECT systimestamp + HorizonWindowMinutes * Interval '1' MINUTE
INTO RETVALUE
FROM DUAL;

Resources