Creating a user defined function in oracle SQL called lastnamefirst - sql

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;

Related

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.

Getting CLOB completed from DB table via SQL in Oracle

I have a table (actually a view) with few varchar2 fields. Say,
v_report(id, name, profile_id, profile_name);
I need to collect id-groupped data into a string. So, I do:
SELECT
id,
'Name: ' || clobagg(DISTINCT name)
|| ' Profile_name: ' || clobagg(DISTINCT profile_name)
as description
FROM
v_report
GROUP BY
id
clobagg is like described here: https://community.oracle.com/thread/2264483
(especially use clobagg not stragg due to it's returning value clob able to store >4000 chars)
It works fine when concatenated profile string isn't too large. But if it is i get fair Oracle exception:
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 7680, maximum: 4000)
Is there some way out? I mean something like DBMS_LOB.APPEND function but available to call via SQL. Or any other way to concat many varchar2 strings into a large CLOB string w/o length limit.
Thanks in advance.
Pavel.
NOTE: initially posted as a Comment, but offered as an Answer now, since the OP has confirmed that this was, in fact, the problem.
So, are you able to create the CLOBs in the first place (without concatenation)? If you are, then what you are missing is wrapping the literals (and any other VARCHAR2 values you may have) within to_clob(). Good luck!
Minor addition.
After this solving I got same error in clobagg;
So I had to modify it to set input parameter as varchar2 (to enable DISTINCT clause work) and the returning value is CLOB.
So, as far it may be useful here it is:
create or replace
TYPE CLOBAGG_TYPE AS OBJECT
(
text clob,
static function ODCIAggregateInitialize(
sctx in out clobagg_type
)
return number,
member function ODCIAggregateIterate(
self in out clobagg_type,
value in varchar2
)
return number,
member function ODCIAggregateTerminate(
self in clobagg_type,
returnvalue out clob,
flags in varchar2
)
return number,
member function ODCIAggregateMerge(
self in out clobagg_type,
ctx2 in clobagg_type
)
return number
)
create or replace
TYPE BODY CLOBAGG_TYPE AS
static function ODCIAggregateInitialize(
sctx in out clobagg_type
)
return number
is
begin
sctx := clobagg_type(null) ;
return ODCIConst.Success ;
end;
member function ODCIAggregateIterate(
self in out clobagg_type,
value in varchar2
)
return number
is
begin
self.text := self.text || ', ' || value;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(
self in clobagg_type,
returnvalue out clob,
flags in varchar2
)
return number
is
begin
returnValue := ltrim(self.text,', ');
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(
self in out clobagg_type ,
ctx2 in clobagg_type
)
return number
is
begin
self.text := self.text || ctx2.text;
return ODCIConst.Success;
end;
END;
create or replace
FUNCTION CLOBAGG(
input varchar2
) RETURN clob
deterministic
parallel_enable
aggregate using clobagg_type;

PLSQL SELECT INTO FROM parameter

I created a function that should return the max id from a table(parameter)
CREATE OR REPLACE FUNCTION getmaxid
(
P_TABLE IN VARCHAR2
)
RETURN NUMBER IS
v_maxId NUMBER(38);
BEGIN
SELECT MAX(id) INTO v_maxId FROM P_TABLE;
RETURN v_maxId;
END getmaxid
However, i keep getting the error message "ORA-00942: table or view does not exist" on this line:
SELECT MAX(id) INTO v_maxId FROM P_TABLE;
Like explained earlier, you need to use dynamic SQL to perform the operation. In this case, p_table is a variable. The solution to this is to build a string that will contain the SQL and dynamically execute it one you've build the query.
The example below uses, DUAL, but the table name is arbitrary.
Here is what you're looking for, take the function outside of the block, I left it like this so that you can test it..
DECLARE
FUNCTION getmaxid (p_table IN VARCHAR2)
RETURN NUMBER
IS
v_maxid NUMBER (38);
v_select VARCHAR2 (200);
cnt SYS_REFCURSOR;
BEGIN
v_select := 'SELECT COUNT(*) FROM ' || p_table;
DBMS_OUTPUT.put_line (v_select);
EXECUTE IMMEDIATE v_select INTO v_maxid;
RETURN v_maxid;
END getmaxid;
BEGIN
DBMS_OUTPUT.put_line (getmaxid ('DUAL'));
END;

local function inside PL/SQL script

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.