Table Valued function in Sql developer - oracle

I have Created a table valued function in Sql server 2012 which works fine
but when i applied same code on same table but with Oracle Sql developer
it shows syntax error
Function in SQL Server
Create Function fx_Mfr
(
#Mfr varchar
)
returns table
as
Return
Select * from Manufacturer
where Mfr = #Mfr

One option is to return ref cursor, such as in this example (which, kind of, simulates what you have in MS SQL Server):
SQL> create or replace function fx (par_deptno in number)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for
7 select deptno, ename, job, sal
8 from emp
9 where deptno = par_deptno;
10 return rc;
11 end;
12 /
Function created.
SQL> select fx(10) from dual;
FX(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB SAL
---------- ---------- --------- ----------
10 CLARK MANAGER 2450
10 KING PRESIDENT 10000
10 MILLER CLERK 1300
SQL>

Or using implicit statement results - if you're on Oracle 12c or higher version Database. This feature was added to make migrating your code from SQL Server easier.
CREATE OR REPLACE PROCEDURE fx (par_deptno IN NUMBER)
AS
l_cursor_1 SYS_REFCURSOR;
BEGIN
OPEN l_cursor_1 FOR
SELECT department_id, first_name, last_name, job_id, salary
FROM employees
WHERE department_id = par_deptno;
DBMS_SQL.RETURN_RESULT(l_cursor_1);
END;
/
And then execute the program
DECLARE
PAR_DEPTNO NUMBER;
BEGIN
PAR_DEPTNO := 100;
FX(
PAR_DEPTNO => PAR_DEPTNO
);
END;
The output is auto-magically returned from the database:
Examples/docs on Oracle-Base

Related

How to create an oracle plsql function that will return multiple rows for a specific parameter (ex: employee id)

I need to create a function that will be called on oracle screen.
I just don't know how to do it
A simple option is function that returns refcursor.
This is example based on Scott's sample emp table; function accepts department number as a parameter and returns data related to employees working in that department.
SQL> create or replace function f_test (par_deptno in emp.deptno%type)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 select deptno, empno, ename, job, sal
8 from emp
9 where deptno = par_deptno;
10 return l_rc;
11 end;
12 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO EMPNO ENAME JOB SAL
---------- ---------- ---------- --------- ----------
10 7782 CLARK MANAGER 2572.5
10 7839 KING PRESIDENT 5250
10 7934 MILLER CLERK 1365
SQL>

Oracle equivalent for a Postgres function TEXT[] parameter, and ANY operator?

Am porting a Postgres PLPGSQL function to an Oracle stored procedure, but it has a TEXT[] array as a parameter, then uses this parameter in an ANY operation - specifically:
create or replace function func_name
(
...
in_array_param text[],
...
)
as $$
declare
begin
...
select column_1,
column_2
from table_x
where (column_a = value)
and (column_b = any(in_array_param));
...
end;
I don't recall Oracle historically having an equivalent to this (using a PL/SQL array in SQL), but with the evolution of PL/SQL, am wondering if there are now options to do this? Am using Oracle 19c, but can move to 21c if there is a newer construct that would help.
I have tried using a VARRAY, and using the IN operator:
type my_array_type is varray(10) of varchar2(255);
var_array my_array_type := my_array_type('1', '2', '3');
procedure my_test(param_a varchar2, param_b my_array_type) is
var_col test_tab.col2%type;
begin
select col2 into var_col
from test_tab
where col1 in param_b;
end my_test;
my_test('2', var_array);
This results in a PLS-00642 error:
ORA-06550: line 11, column 21:
PLS-00642: local collection types not allowed in SQL statements
Have a look at the following example.
It uses built-in datatype (use it; why creating your own, if you don't have to?). Procedure searches through employees and finds the last (alphabetically) employee name in list of jobs passed as a collection.
I'd say that it is line #10 you're looking for.
SQL> create or replace procedure p_test
2 (param_a in number, param_b in sys.odcivarchar2list)
3 is
4 l_ename emp.ename%type;
5 begin
6 select max(e.ename)
7 into l_ename
8 from emp e
9 where e.deptno = param_a
10 and e.job in (select * from table(param_b));
11 dbms_output.put_line('Result: ' || l_ename);
12 end;
13 /
Procedure created.
Sample data:
SQL> select ename, job from emp where deptno = 20 order by job, ename;
ENAME JOB
---------- ---------
FORD ANALYST --> this
SCOTT ANALYST --> this
ADAMS CLERK
SMITH CLERK
JONES MANAGER --> this
Who's the last among analysts and managers? These are Ford, Jones and Scott so - Scott it is:
SQL> set serveroutput on
SQL> exec p_test(param_a => 20, param_b => sys.odcivarchar2list('ANALYST', 'MANAGER'));
Result: SCOTT
PL/SQL procedure successfully completed.
SQL>

don;t know why code is not working oracle live sql ,getting errors like ORA-06512: at "SYS.DBMS_SQL", line 1721

DECLARE
v_annual_salary NUMBER;
BEGIN
SELECT SAL * 12 INTO v_annual_salary
FROM EMP,DEPT
WHERE EMPNO = 7722;
DBMS_OUTPUT.PUT_LINE ('Annual Salary of 7722 is ' || TO_CHAR(v_annual_salary));
END;
/
Unless DEPT table contains a single row, this query will return TOO_MANY_ROWS. Why do you cross-join EMP and DEPT? Should have been just
SQL> declare
2 v_annual_salary number;
3 begin
4 select sal * 12
5 into v_annual_salary
6 from emp
7 where empno = 7934;
8
9 dbms_output.put_line('Annual salary of 7934 is ' || to_char(v_annual_salary));
10 end;
11 /
Annual salary of 7934 is 15600
PL/SQL procedure successfully completed.
SQL>
(My EMP table doesn't have employee whose EMPNO = 7722 so I used 7934).

pl/sql stored procedure to select from a table where date is equal to stored procedure parameter

I am new to PL/SQL and oracle, I am using SQL developer 19 against an Oracle 12C database.
All I am trying to do, as I am used to do in T-SQL, is to select some data from a table where a date field value is between two stored procedure ate parameters; below the stored procedure I am using that give to me an error saying that I have to "SELECT INTO" ??
create or replace PROCEDURE GET_DMR_HALO_VALUES ( START_DATE IN DATE , END_DATE IN DATE )
AS
BEGIN
SELECT
HALO_RECORD_ID ,
ASSET_ID ,
ASSET_NAME ,
NUMERIC_VALUE ,
IS_ENABLED ,
ADDED_BY ,
VALUE_DATE ,
NOTES ,
DATE_ADDED
FROM halo_inputs
WHERE trunc(value_date) BETWEEN START_DATE and END_DATE;
END GET_DMR_HALO_VALUES;
then I also have another problem... assuming that the above works I am trying to view the returned table data by calling the stored procedure in SQL developer as follow
DEFINE START_DATE date := TO_DATE('2019-02-12','YYYY-DD-MM');
DEFINE END_DATE date := TO_DATE('2019-02-12','YYYY-DD-MM');
exec GET_DMR_HALO_VALUES(:START_DATE, :END_DATE );
Am I right by calling the SP like above?
UPDATE
after looking at this stack-over article
I changed the stored procedure as follow
create or replace PROCEDURE GET_DMR_HALO_VALUES ( START_DATE IN DATE , END_DATE IN DATE )
AS
c1 sys_refcursor;
BEGIN
open c1 for
SELECT
HALO_RECORD_ID ,
ASSET_ID ,
ASSET_NAME ,
NUMERIC_VALUE ,
IS_ENABLED ,
ADDED_BY ,
VALUE_DATE ,
NOTES ,
DATE_ADDED
FROM halo_inputs
WHERE trunc(value_date) BETWEEN START_DATE and END_DATE;
dbms_sql.return_result(c1);
END;
but are cursors the only way in Oracle to get table data ?
UPDATE 2
I also changed the exec query as follow
DECLARE
START_DATE date := TO_DATE('12-02-20','DD-MM-YY');
END_DATE date := TO_DATE('12-02-20','DD-MM-YY');
BEGIN
GET_DMR_HALO_VALUES(START_DATE,END_DATE );
END;
and it works but how I get SQL developer to display data in a grid view rather than in plain text ?
The simplest option to make this query return data in data grid is to ... well, run it outside of the stored procedure. Procedures are nice, but not that nice for doing what you want.
When returning something, a function might be a better choice. For example:
Create it first:
SQL> create or replace FUNCTION f_test (par_deptno in number)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 select empno, ename, job, sal
8 from emp
9 where deptno = par_deptno;
10 return l_rc;
11 end;
12 /
Function created.
Then call it:
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 5000
7934 MILLER CLERK 1300
SQL>
In SQL Developer, you'd run the last statement (select f_test(10) from dual;) as a script (F9 keyboard shortcut) and it'll then display the result nicely (as my example shows above, although it is SQL Plus command line tool output). Though, it still won't be in a data grid.

How do I pass a list of numbers into a stored procedure?

So I have the following stored procedure:
CREATE OR REPLACE PROCEDURE stored_p
(
ntype IN NUMBER ,
p_ResultSet OUT TYPES.cursorType
)
AS
BEGIN
OPEN p_ResultSet FOR
select * from table where ttype in ntype;
END stored_p
and, I can call it like this:
VARIABLE resultSet REFCURSOR
EXEC stored_p(80001, :resultSet);
PRINT :resultSet
but I want to be able to call it like this:
VARIABLE resultSet REFCURSOR
EXEC stored_p([80001,80002], :resultSet);
PRINT :resultSet
How should I modify my stored procedure accordingly? I am doing this so that I can display the results in a Crystal Report... (just in case that affects anything).. Thanks!!
The best option would be to pass a collection
SQL> create type empno_tbl
2 is
3 table of number;
4 /
Type created.
SQL> create or replace procedure stored_p
2 (
3 empnos in empno_tbl,
4 p_rc out sys_refcursor )
5 as
6 begin
7 open
8 p_rc for select * from emp where empno in (select * from table(empnos));
9 end;
10 /
Procedure created.
SQL> var rc refcursor;
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure stored_p
2 (
3 empnos in empno_tbl,
4 p_rc out sys_refcursor )
5 as
6 begin
7 open
8 p_rc for select * from emp where empno in (select * from table(empnos));
9* end;
SQL> begin
2 stored_p( new empno_tbl(7902,7934), :rc );
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO FAKE_COL FOO
---------- ---------- ----------
7902 FORD ANALYST 7566 03-DEC-81 3000
20 1
7934 MILLER CLERK 7782 23-JAN-82 1300
10 1
Unfortunately, Crystal Reports may not be able to pass a proper collection to a stored procedure. If that is the case, you'd have to pass in a comma-separated list of numbers. Your procedure would then have to parse that comma-separated string into a collection. You can use (or modify) Tom Kyte's in_list function for this
SQL> ed
Wrote file afiedt.buf
1 create or replace function in_list(
2 p_string in varchar2
3 )
4 return empno_tbl
5 as
6 l_string long default p_string || ',';
7 l_data empno_tbl := empno_tbl();
8 n number;
9 begin
10 loop
11 exit when l_string is null;
12 n := instr( l_string, ',' );
13 l_data.extend;
14 l_data(l_data.count) :=
15 ltrim( rtrim( substr( l_string, 1, n-1 ) ) );
16 l_string := substr( l_string, n+1 );
17 end loop;
18 return l_data;
19* end;
SQL> /
Function created.
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure stored_p
2 (
3 empnos in varchar2,
4 p_rc out sys_refcursor )
5 as
6 begin
7 open p_rc
8 for select *
9 from emp
10 where empno in (select *
11 from table(in_list(empnos)));
12* end;
SQL> /
Procedure created.
SQL> ed
Wrote file afiedt.buf
1 begin
2 stored_p( '7902,7934', :rc );
3* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- --------- ---------- ----------
DEPTNO FAKE_COL FOO
---------- ---------- ----------
7902 FORD ANALYST 7566 03-DEC-81 3000
20 1
7934 MILLER CLERK 7782 23-JAN-82 1300
10 1
Newer versions might have different options. I work some with Oracle 9 and 10, and I will typically pass in a string of comma-separated values and dynamically build the SQL. There are some significant dangers with SQL injection to be aware of, though.
You need to create a type..
create or replace type NUMBER_ARRAY as table of number;
CREATE OR REPLACE PROCEDURE stored_p
(
ntype IN NUMBER_ARRAY ,
p_ResultSet OUT TYPES.cursorType
)
You can loop it using..
for i in 1 .. ntype.count
loop
dbms_output.put_line( ntype(i) );
end loop;
To test it,
DECLARE
ntypetest NUMBER_ARRAY := NUMBER_ARRAY ();
BEGIN
FOR i IN 1 .. 5
LOOP
ntypetest.EXTEND;
ntypetest (i) := i;
END LOOP;
stored_p(ntypetest,..)
There may be some variation in syntax.
Of course you can pass in comma separated values too but that will come in as a string. Your string should be something like 'val1','val2','val3'. You need to be careful when you have numbers as the whole string will look like in ('2343,3444,2222') which will be treated as one value instead of multiple numbers as in (2343,3444,2222)

Resources