Does Oracle SQL support output parameters in system functions? - sql

In Oracle SQL, do any of the system functions (listed on http://docs.oracle.com/database/121/SQLRF/functions001.htm) support output parameters, or do they only return a value?
I know that in Oracle PL/SQL, a function can return values and can contain output parameters, but do any SQL system functions themselves have output parameters?

[TL;DR] Functions with out parameters can only be used in PL/SQL.
As an example:
CREATE FUNCTION test_out(
value OUT NUMBER
) RETURN NUMBER
IS
BEGIN
value := 1;
RETURN 2;
END;
/
You can run this in PL/SQL:
SET SERVEROUTPUT ON;
DECLARE
a NUMBER;
b NUMBER;
BEGIN
a := test_out( b );
DBMS_OUTPUT.PUT_LINE( a || ', ' || b );
END;
/
Outputs 2, 1
However, trying to do the same thing in SQL (creating a bind variable via Oracle's SQL Developer):
VARIABLE a NUMBER;
SELECT test_out( :a ) FROM DUAL;
PRINT a;
Gives you the exception:
SQL Error: ORA-06572: Function TEST_OUT has out arguments
06572. 00000 - "Function %s has out arguments"
*Cause: A SQL statement references either a packaged, or a stand-alone,
PL/SQL function that contains an OUT parameter in its argument
list. PL/SQL functions referenced by SQL statements must not
contain the OUT parameter.
*Action: Recreate the PL/SQL function without the OUT parameter in the
argument list.

Related

PL/SQL equivalent of SELECT statement

What would be the PL/SQL equivalent of this SQL query:
SELECT * FROM table(OWNER.PACKAGE.get_exam('123456789'));
This is the Function that I am trying to call:
FUNCTION get_exam(id IN VARCHAR2)
RETURN ab_assign_v1
IS
CURSOR c_exams(cid VARCHAR2) IS
SELECT t_api_exam_v1(
sei.person_id, --unique id
l.description --loc description
)
FROM my_view sei
JOIN loc l
ON sei.loc_code = l.loc_code
v_collection ab_assign_v1;
BEGIN
OPEN c_exams(id);
FETCH c_exams BULK COLLECT INTO v_collection;
CLOSE c_exams;
RETURN v_collection;
EXCEPTION
WHEN OTHERS THEN
error_a1.raise_error(SQLCODE, SQLERRM);
END get_exam;
Hope this helps.
DECLARE
lv <COLLECTION_NAME>;
BEGIN
lv:= OWNER.PACKAGE.get_exam('123456789');
dbms_output.put_line(lv.COUNT);
END;
/
Assuming that you want to return the result of a function :
select owner.package.get_exam('123456789') from table
Your function returns a nested table type. You simply need to declare a variable of that type, and assign to it as you would if it were a scalar:
declare
l_coll your_collection_type;
begin
l_coll := OWNER.PACKAGE.get_exam('123456789');
end;
/
In this example your_collection_type is a placeholder for whatever object your function actually returns.
" I am getting this error: PLS-00201: identifier 'ab_assign_v1' must be declared "
ab_assign_v1 is the type used by your function. From the code posted in your revised question it seems that type is in the same schema which owns the package with the function. However your original pseudo-code prefixes the call with the schema name. So, putting two and two together, you need to revise the variable declaration to include the schema too. (You may need to grant EXECUTE on it too, if you haven't done this already).
declare
l_coll OWNER.ab_assign_v1;
begin
l_coll := OWNER.PACKAGE.get_exam('123456789');
end;
/

ORA-00900: invalid SQL statement error?What's wrong with my sql?

I try to write a function that split the string,but it shows:ORA-00900: invalid SQL statement error.What's wrong?I think that v_str varchar2(500); or v_strs_last varchar2(4000) := p_value; may be wrong.
CREATE OR REPLACE TYPE strsplit_type IS TABLE OF VARCHAR2 (4000);
create or replace function strsplit(p_value varchar2,
p_split varchar2 := ',')
return strsplit_type
pipelined is
v_idx integer;
v_str varchar2(500);
v_strs_last varchar2(4000) := p_value;
begin
loop
v_idx := instr(v_strs_last, p_split);
exit when v_idx = 0;
v_str := substr(v_strs_last, 1, v_idx - 1);
v_strs_last := substr(v_strs_last, v_idx + 1);
pipe row(v_str);
end loop;
pipe row(v_strs_last);
return;
end strsplit;
My oracle version is Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production.And I run the script in DB SOLO 5.
The error picture is:
Your DB Solo client seems to be interpreting the first semicolon it sees as the end of the statement, which is reasonable for plain SQL (DML or DDL) but not for PL/SQL.
You can see that from the log image you posted; it treats the create function ... v_ids integer part as one statement because that ends with the first semicolon - it compiles but with an error. Then it takes the next chunk, up to the next semicolon, as a separate statement - v_str varchar2(5000) - and it's that which gets the ORA-00900, since it is not valid SQL.
According to the documentation:
The statements must be separated by either semicolon or the 'GO' keyword. You can change the settings to only use a semicolon or 'GO' as statement separator instead of the default setting which accepts either one. When you have more than one statement in the Query Editor, DB Solo will send them to your server one at a time.
So based on that it doesn't seem to understand how to treat PL/SQL differently; but you can change your settings to not treat the semicolons as statement separators - across the board, which means you'd need to add GO after both the create type and create function statements, and any other queries or calls you make. This would be similar to using / everywhere in SQL*Plus or SQL Developer.
It may be easier to use the procedure editor. Presumably after you've created the type and function from the object browser.
Or, of course, use a different client...

How to declare a function in a PLSQL block and call same in a select query?

I have a code like this-:
DECLARE
--BEGIN
V_DATE DATE;
FUNCTION GETDATE(NUM_DAYS NUMBER) RETURN DATE IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LEAVE.GES_LEV_REQ_PURG_ERR VALUES (NUM_DAYS, NUM_DAYS, SYSDATE);
COMMIT;
RETURN(SYSDATE);
END GETDATE;
BEGIN
--V_DATE := GETDATE(1);
SELECT GETDATE(1) INTO V_DATE FROM DUAL;
DBMS_OUTPUT.PUT_LINE(V_DATE);
END;
/
But this is throwing below error-
ORA-06550: line 17, column 10:
PLS-00231: function 'GETDATE' may not be used in SQL
ORA-06550: line 17, column 10:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 17, column 3:
PL/SQL: SQL Statement ignored
While if I am calling that function without select query, it works fine.
Please help.
Nested sub programs can be used in the scope of the procedure where it has been created. Nested subprograms cannot be used in SQL since it is not directly available as DB object. Please be aware that there are two engines executing your PL/SQL block(SQL Engine & PL/SQL Engine). While executing your query with nested function, SQL engine tries to match the function name with the DB object. But in your case it is a subprogram inside a procedure. Hope I am understandable.
And as you said, you could perform DML inside a function and it could be used in SQL provided it is declared as PRAGMA AUTONOMOUS_TRANSACTION
There are Basically two thing which need to ask before we proceed for
any answer. 1) What is the exact use for this kind of call or block.
2) Why you are not going for FUCNTION creation first and then call it.
NOTE : [ Anyhow you cannot call a FUNCTION in SELECT statement if it
has DML operations involved in it. ]
I will try to illustrate the ay it should work. Let me know if it
helps
--Create Function
CREATE OR REPLACE FUNCTION GETDATE(
NUM_DAYS NUMBER)
RETURN DATE
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LEAVE.GES_LEV_REQ_PURG_ERR VALUES
(NUM_DAYS, NUM_DAYS, SYSDATE
);
COMMIT;
RETURN(SYSDATE);
END GETDATE;
-- Call the function but not in a SQL statement.
set serveroutput on;
DECLARE
lv_date DATE;
BEGIN
lv_date:=GETDATE();
dbms_output.put_line(lv_date);
END;

Dynamic sql with bind variable

/
create or replace procedure search_proc(p_string varchar2,p_table varchar2,p_col varchar2,search_result OUT sys_refcursor)
is
SQL_QRY VARCHAR2(2000);
BEGIN
SQL_QRY:='SELECT EMPNO,:1 FROM :2';
--DBMS_OUTPUT.PUT_LINE('SQL:'||SQL_QRY);
OPEN SEARCH_RESULT FOR SQL_QRY USING p_col,p_table;
END;
/
VARIABLE REFC REFCURSOR;
EXEC SEARCH_PROC('TEST','EMP','ENAME',:REFC);
PRINT REFC;
/
I am trying to return empno and employee name using a procedure which contains dynamically built SQL query .The query is built using bind variables.but getting the following error.May be something is wrong with the way i am calling the procedure
ORA-06512: at line 1 00903. 00000 - "invalid table name"
You can't use bind variables to take the place of identifiers, such as table names or column names. Those things must be known at the time the statement is parsed, which occurs before bind variables are bound to values. (Part of the whole purpose of using bind variables is to be able to parse a statement once then execute it with variable values.)
In this case, the solution is simple, since you are already putting the query string into a variable.
BEGIN
SQL_QRY:='SELECT EMPNO,' || p_col || ' FROM ' || p_table;
--DBMS_OUTPUT.PUT_LINE('SQL:'||SQL_QRY);
OPEN SEARCH_RESULT FOR SQL_QRY;

Oracle SQL Developer, using dynamic SQL in function

I have the following script which contains a function named 'myFunction'. (declaration of types named rowValueTmp and rowValueTable are also attached for your information) Basically, I need to use a table name as an input parameter for myFunction. I found that I need to use dynamic SQL in order to use the table name as a parameter (Please correct me if there are alternative ways to do this). So the following code is what I have tried so far.
create or replace type rowValueTmp as object (
month number,
year number
);
/
create or replace type rowValueTable as table of rowValueTmp;
/
create or replace FUNCTION myFunction (TABLENAME in VARCHAR2)
return rowValueTable as
v_ret rowValueTable;
begin
execute immediate '
select rowValueTmp(month, year)
bulk collect into v_ret
from '||TABLENAME;
return v_ret;
end myFunction;
/
select * from table(myFunction('SCHEMA.TEST'));
But, this code gives me an error, and I assumed that this error is occurred because of using 'bulk collect' in execute immediate block.
ORA-03001: unimplemented feature
If I replace the content of execute immediate as the following, the above script is working..
select rowValueTmp(month, year)
bulk collect into v_ret
from SCHEMA.TEST;
Question
1] Is there any way(rather than Dynamic SQL) that I can use a table name as an input parameter for myFunction?
2] If I am not allowed to use bulk collect in execute immediate block, what do you suggest?
You can return values from execute immediately into a bulk collect:
CREATE OR REPLACE FUNCTION myfunction (tablename IN VARCHAR2)
RETURN rowvaluetable AS
v_ret rowvaluetable;
v_table VARCHAR2 (61) := DBMS_ASSERT.sql_object_name (tablename);
BEGIN
EXECUTE IMMEDIATE '
select rowValueTmp(month, year)
from ' || v_table
BULK COLLECT INTO v_ret;
RETURN v_ret;
END myfunction;
/
In the interest of an abundance of caution, I'd recommend using DBMS_ASSERT to validate the table parameter as well (as shown).