local function inside PL/SQL script - sql

I'm trying to execute this code in Oracle 10 SQL Developer:
FUNCTION is_valid_date (p_val in VARCHAR2, p_format IN VARCHAR2 )
RETURN numeric IS
l_date VARCHAR2(100);
BEGIN
l_date := TO_date( p_val, p_format );
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END is_valid_date;
BEGIN
DBMS_OUTPUT.PUT_LINE(is_valid_date('20120101', 'YYYYMMDD' ));
END;
but I get a generic error without any specific Oracle code, as if it is a syntax problem.
I need to check if a date is valid and, as there is no Oracle built in function for that, I have defined it inside my script (I don't want it to be global or stored somewhere).
Edit:
I have found a solution on an oracle forum using oracle regexp, instead of a function. My script is:
BEGIN
select * from mytable where not REGEXP_LIKE(mydatefield, '(((0[1-9]|[12]\d|3[01])\.(0[13578]|1[02])\.((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\.(0[13456789]|1[012])\.((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\.02\.((19|[2-9]\d)\d{2}))|(29\.02\.((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))')
END;
where mydatefield is in the format DD.MM.YYYY

If that's your entire script, you're missing the DECLARE keyword at the start of your anonymous block:
DECLARE
FUNCTION is_valid_date (p_val in VARCHAR2, p_format IN VARCHAR2 )
RETURN numeric IS
l_date VARCHAR2(100);
BEGIN
l_date := TO_date( p_val, p_format );
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END is_valid_date;
BEGIN
DBMS_OUTPUT.PUT_LINE(is_valid_date('20120101', 'YYYYMMDD' ));
END;
/
anonymous block completed
1
Without that you'll get a series of errors starting with
Error starting at line : 1 in command -
FUNCTION is_valid_date (p_val in VARCHAR2, p_format IN VARCHAR2 )
Error report -
Unknown Command
... which I imagine is the 'generic error' you referred to.

Related

Error "Encountered the symbol "IN" when expecting one of the following" in Oracle

This is the code:
CREATE PROCEDURE print_string(IN input_string VARCHAR(255))
BEGIN
DECLARE num_chars INT DEFAULT 0;
IF input_string IS NULL THEN
SET num_chars = 0;
ELSE
SET num_chars = CHAR_LENGTH(input_string);
END IF;
SELECT UPPER(input_string), num_chars;
END;
I get error:
PLS-00103: Encountered the symbol "IN" when expecting one of the following: <an identifier> <a double-quoted delimited-identifier>
current delete exists prior
Errors: check compiler log
How do I fix: current delete exists prior?
The immediate error is that you have the argument name and mode the wrong way around - it should be (input_string IN ... not (IN input_string .... But there are other problems:
Oracle recommends VARCHAR2 over VARCHAR.
arguments just have the data type, not a size (or precision/scale), so it should be (input_string IN VARCHAR2) not (input_string IN VARCHAR2(255).
you are missing the IS/AS keyword.
DECLARE comes before BEGIN in a PL/SQL block; having a nested block here would be valid, but you're missing a BEGIN and END; if you do that, and it isn't necessary so I don't think it's what you meant. And you don't need the DECLARE at all for a procedure, it's implied.
if you want a default value for a PL/SQL variable then assign it, rather than using DEFAULT. (You don't really need to do this here, as you always assign a value later anyway, but I'm sticking with your general approach.)
it's probably better to use native Oracle types, so NUMBER or PLS_INTEGER instead of INT.
assignment of values is with :=, not SET ... = ....
CHAR_LENGTH should just be LENGTH (unless you have your own function with that name).
in PL/SQL you have to select into something, and from something. But if you do that here, you still have to return it to the caller somehow.
given that you want to 'print' the string, you probably want dbms_output - though that relies on the client showing the result, which most don't by default, and it's generally only used for debugging...
So this would work:
CREATE PROCEDURE print_string(input_string IN VARCHAR2) AS
num_chars PLS_INTEGER := 0;
BEGIN
IF input_string IS NULL THEN
num_chars := 0;
ELSE
num_chars := LENGTH(input_string);
END IF;
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ': ' || num_chars);
END;
/
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
END;
/
1 rows affected
dbms_output:
THIS IS A TEST: 14
fiddle
But again, dbms_output isn't ideal. And it could be done much more simply (#Mto has shown one way), or without using PL/SQL at all.
You can fix the issues (listing in #Alex Poole's answer) and simplify the procedure to:
CREATE PROCEDURE print_string(
input_string IN VARCHAR2
)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ', ' || COALESCE(LENGTH(input_string), 0));
END;
/
Then:
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
print_string(NULL);
END;
/
Outputs:
THIS IS A TEST, 14
, 0
fiddle
The code syntax is incorrect here. It should be something like
CREATE OR REPLACE PROCEDURE print_string(input_string IN VARCHAR2)
IS
BEGIN

Oracle passing a variable to a function

I have some SQL code, which uses a hard coded date. My goal is to remove the hard coded dates and replace them with variables to make the code generic.
My test CASE is the following;
CREATE OR REPLACE FUNCTION IsDate( p_str IN VARCHAR2, p_format IN VARCHAR2 ) RETURN NUMBER AS
V_date DATE;
BEGIN
V_Date := TO_DATE( p_str, p_format );
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
This works fine
select IsDate(DATE '2021-07-31','YYYY-MM-DD') from dual;
This causes an error (see below). Can someone please explain the issue and how can it be fixed.
I'm testing on live SQL. Thanks in advance to all who answer.
exec :my_date := DATE '2021-07-31';
select IsDate( DATE my_date,'YYYY-MM-DD') from dual;
i am getting below error
ORA-01008: not all variables bound ORA-06512: at "SYS.DBMS_SQL", line 1721
ORA-00936: missing expression
There a special function VALIDATE_CONVERSION since 12.2 that does exactly what you want to achieve:
VALIDATE_CONVERSION
Example:
SQL> select VALIDATE_CONVERSION('01-01-2000' as date,'dd-mm-yyyy') chk1 from dual;
CHK1
----------
1
SQL> select VALIDATE_CONVERSION('01-01-2000' as date,'yyyy-mm-dd') chk2 from dual;
CHK2
----------
0
Also livesql.oracle.com doesn't support exec command which is an SQL*Plus command. So if you want to test your functions with bind variables, you can use simple PL/SQL Variables:
declare
string_date varchar2(100);
FUNCTION IsDate( p_str IN VARCHAR2, p_format IN VARCHAR2 ) RETURN NUMBER AS
V_date DATE;
BEGIN
V_Date := TO_DATE( p_str, p_format );
RETURN 1;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
begin
string_date:='31-12-2000';
dbms_output.put_line(IsDate(string_date,'dd-mm-yyyy'));
end;
/
The DATE keyword expects a literal, you cannot use a string variable.
Regarding your function:
FUNCTION IsDate( p_str IN VARCHAR2, p_format IN VARCHAR2 ) RETURN NUMBER
It expects two VARCHAR2, i.e. string values. However when you run IsDate(DATE '2021-07-31','YYYY-MM-DD') the you pass a DATE value into the function, not a string.
Checking the format of a DATE value is pointless, because a DATE values itself has no format, it is stored as an internal binary value. Never use TO_DATE on a value which is already a DATE. Function TO_DATE expects a string which shall be converted to a DATE.
What is displayed when you select a DATE is defined (by default) with your current user session NLS_DATE_FORMAT setting. You can change the default output format for example with ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YYYY'; or you set it explicitly by using the TO_CHAR() function.
When you call your function IsDate(DATE '2021-07-31','YYYY-MM-DD') then Oracle makes an implicit cast to a string. i.e. it runs
V_Date := TO_DATE( TO_CHAR(p_str, SYS_CONTEXT('USERENV', 'NLS_DATE_FORMAT')), p_format );
When you pass a DATE value, then your function actually acts like this:
CREATE OR REPLACE FUNCTION IsDate( p_str IN VARCHAR2, p_format IN VARCHAR2 ) RETURN NUMBER AS
ret NUMBER;
BEGIN
SELECT COUNT(*)
INTO ret
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT'
AND VALUE = p_format;
RETURN ret;
-- p_str is ignored completely
END;
Note, in Oracle 18 the TO_DATE function provides the DEFAULT ... ON CONVERSION ERROR clause, so it may be an overkill to write the extra function.

PLS-00382: expression is of wrong type. PL/SQL: Statement ignored

I tried executing a simple package with function overloading. Below is the package code.
--package specification:
create or replace package over_load as
FUNCTION print_it(v_date date) return date;
FUNCTION print_it(v_name varchar2) return number;
end over_load;
--package body:
create or replace package body over_load as
FUNCTION print_it(v_date date) return date is --function accepting and returning date
begin
dbms_output.put_line('the date is ' || v_date);
return v_date;
end print_it;
FUNCTION print_it(v_name varchar2) return number is /*function accepting string and returning number*/
v_eno employees.employee_id%type;
begin
select employee_id into v_eno from employees where first_name = v_name;
return v_eno;
end print_it;
end over_load;
I tried executing the first function in the package using the below anonymous block.
declare
sample_date date;
begin
sample_date := over_load.print_it('14-07-2017');
dbms_output.put_line(sample_date);
end;
I tried passing date as the argument to the first function, but it throws the wrong argument type error. Any idea on why?
If the procedure (or a function) expects DATE datatype, then don't pass string to it. Because, '14-07-2017' is a string.
SQL> set serveroutput on
SQL>
SQL> declare
2 sample_date date;
3 begin
4 --sample_date := over_load.print_it('14-07-2017');
5 sample_date := over_load.print_it(date '2017-07-14');
6 dbms_output.put_line(sample_date);
7 end;
8 /
the date is 14.07.17
14.07.17
PL/SQL procedure successfully completed.
SQL>
In line #5, I passed a date literal. It could have also been to_date('14-07-2017', 'dd-mm-yyyy').
Oracle - if it can - implicitly converts what you pass to datatype it expects, but it doesn't always succeed; that depends on NLS settings.
To be sure that it'll ALWAYS work, take control over it and use appropriate datatypes.

Procedure invalid identifier when trying to use in SQL / PLSQL

i'm trying to make a very basic procedure on PLSQL but when i try to use it in SQL it returns invalid identifier.
create or replace PROCEDURE YEARS_BETWEEN(date1 IN date , date2 IN date, p_result out number)
IS
v_months number;
BEGIN
v_months := months_between(date1, date2);
p_result := TRUNC(v_months / 12, 0);
END years_between;
Can anyone tell me whats wrong?
SQL IS
select YEARS_BETWEEN(GBDATUM, SYSDATE) as leeftijd FROM medewerkers;
You need a function not a procedure if you want to call it in a select:
create or replace function years_between (in_date1 in date , in_date2 in date)
return number as
v_months number;
begin
v_months := months_between(date1, date2);
return(trunc(v_months / 12, 0));
end; -- years_between

Creating a user defined function in oracle SQL called lastnamefirst

I am using oracle SQL developper.
I am trying to create a function that will accept two parameters (first and last name) and return them as one variable, with the last name showing up first. Here is my function.
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
BEGIN
DECLARE varFullName VARCHAR2;
DEFINE varFullName := CONCAT(varLastName,' ' ,varFirstName);
RETURN varFullName;
END LASTNAMEFIRST;
I am receiving an error on the semicolon at 'end lastnamefirst' "syntax error"
I keep trying to change small things and that same error just shows up in different places whenever I change things. What am I doing wrong?
Wrong syntax. Should be
SQL> create or replace function lastnamefirst
2 (varfirstname in varchar2,
3 varlastname in varchar2)
4 return varchar2
5 as
6 begin
7 return varlastname||' '||varfirstname;
8 end;
9 /
Function created.
SQL> select lastnamefirst('Little', 'Foot') result from dual;
RESULT
------------------------------
Foot Little
SQL>
What's wrong with your code?
you don't DECLARE within the body; if you do, there's no DECLARE keyword at all, and datatype requires length (such as VARCHAR2(30))
CONCAT accepts only two arguments; use a concatenation operator, double pipe || instead
there's no DEFINE in PL/SQL
I would expect the Oracle syntax to look more like:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST (
in_FirstName IN VARCHAR2,
in_LastName IN VARCHAR2
)
RETURN VARCHAR2 AS
v_FullName varchar2(4000);
BEGIN
v_FullName := in_LastName || ' ' || in_FirstName;
RETURN v_FullName;
END; -- LASTNAMEFIRST;
This can of course be simplified (say by not using a local variable), but it follows the logic of your code.
Using DECLARE where you have is essentially starting a new code block, which is leading to the error you see. In your code, the DECLAREisn't necessary if you move the variable declaration prior to the BEGIN. DEFINE is also invalid. Something like this should work:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
varFullName VARCHAR2(100);
BEGIN
varFullName := varLastName || ' ' || varFirstName;
RETURN varFullName;
END LASTNAMEFIRST;
This could be simplified further by removing the variable declaration completely:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
BEGIN
RETURN varLastName || ' ' || varFirstName;
END LASTNAMEFIRST;