Oracle: Returning multiple rows in a function - oracle

I am trying to create a function which would return multiple rows.
Following is my function and type
create or replace type emp_type
(
first_name varchar2(20)
, last_name varchar2(20)
, depart_name varchar2(20)
)
/
create or replace function get_employee
(loc in number)
return emp_type
as
emp_record emp_type;
begin
select a.first_name, a.last_name, b.department_name into emp_record.first_name,
emp_record.last_name,emp_record.depart_name
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
And I used
select get_employee(5) from dual;
I am getting "exact fetch returns more than requested number of rows " error.
Later when I used rownum<2 in the select query I got "Reference to uninitialized composite".
Could you please help?
Thanks in Advance

If you want to return a sys_refcursor, there is no reason to declare the object type or to try to return an object type. Just return a sys_refcursor.
create or replace function get_employee
(p_loc in number)
return sys_refcursor
as
l_rc sys_refcursor;
begin
open l_rc
for select a.first_name, a.last_name, b.department_name
from employees a,
departments b
where a.department_id=b.department_id
and location_id=p_loc;
return l_rc;
end;

Related

How to write PL/SQL function which returns the result of a select statement having as pararmeters the function's parameters?

I tried to write a PL/SQL function having as parameters a tablename and a column name, which returns the result of the query as a table.
Here's what I tried:
CREATE TYPE TABLE_RES_OBJ AS OBJECT (
employee_id number(30) ,
person_id number(30),
joined_year number(4),
salary number(20,2),
qualification varchar2(45),
department varchar2(45)
);
/
create TYPE table_ret as TABLE OF table_res_obj;
/
create or replace function select_custom(p_tablename varchar2, p_colname varchar2 ) return table_ret
is
ret table_ret;
query_txt varchar2(100) := 'SELECT :a from :b';
begin
execute immediate query_txt bulk collect into ret using p_colname, p_tablename;
return ret;
end select_custom;
As you can see, this is not that general as wanted, but still not working, it says the table doesn't exist, even when I try to run it with an existing table.
Exactly, it won't work that way. You'll have to concatenate table and column name into the select statement. For (simple) example:
SQL> create or replace type table_res_obj as object
2 (ename varchar2(20));
3 /
Type created.
SQL> create or replace type table_ret as table of table_res_obj;
2 /
Type created.
SQL> create or replace function select_custom
2 (p_tablename varchar2, p_colname varchar2 )
3 return table_ret
4 is
5 ret table_ret;
6 query_txt varchar2(100);
7 begin
8 query_txt := 'select table_res_obj(' || dbms_assert.simple_sql_name(p_colname) ||') from ' ||
9 dbms_assert.sql_object_name(p_tablename);
10 execute immediate query_txt bulk collect into ret;
11 return ret;
12 end select_custom;
13 /
Function created.
Does it work?
SQL> select select_custom('dept', 'deptno') from dual;
SELECT_CUSTOM('DEPT','DEPTNO')(ENAME)
--------------------------------------------------------------------------------
TABLE_RET(TABLE_RES_OBJ('10'), TABLE_RES_OBJ('20'), TABLE_RES_OBJ('30'), TABLE_R
ES_OBJ('40'))
SQL>

use pipelined function to get result set from multi-tables join

i want to create a function to get selectivity of a table, the input parameter is owner and table name, i searched and find pipelined function can be used here, i used the following codes but i get error for creating function,
create or replace type ROW_TYPE as object
(
column_name varchar2(128),
num_rows number,
num_distinct number
);
/
create or replace type TABLE_TYPE as table of ROW_TYPE;
/
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in(
select a.column_name,b.num_rows,a.num_distinct
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner and a.table_name = b.table_name and a.owner=upper(owner) and
a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_rows,myrow.num_distinct));
end loop;
return;
end;
/
error:
SQL> show errors;
Errors for FUNCTION MOVIL.GETSELECTIVITY:
LINE/COL ERROR
-------- -------------------------------
9/88 PL/SQL: ORA-00918: undefined column
7/14 PL/SQL: SQL Statement ignored
12/20 PLS-00364: Invalid usage of Loop index variable 'MYROW'
12/2 PL/SQL: Statement ignored
but if i just query one single table, then everything is fine,
create or replace type ROW_TYPE as object
(
column_name varchar2(128),
num_distinct number
);
/
create or replace type TABLE_TYPE as table of ROW_TYPE;
/
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in(
select a.column_name,a.num_distinct
from dba_tab_col_statistics a
where a.owner=upper(owner) and a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_distinct));
end loop;
return;
end;
/
does pipelined function do not support multi-tables joining ?
You can create a view on the tables like the below and use it in your function
create or replace view test_db_view as select
a.column_name,b.num_rows,a.num_distinct,
a.table_name,a.owner
from all_tab_col_statistics a, all_tables b
where a.owner = b.owner and a.table_name = b.table_name
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in( select * from test_db_view a where a.owner=upper(owner) and
a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_rows,myrow.num_distinct));
end loop;
return;
end;
OR you can try using p_owner instead of owner in function getSelectivity parameter

how to fetch plsql table type values into refcursor

I am trying to create a procedure in which another procedure is being called and it returns pl/sql table type. How to collect values of plsql table into ref cursor and return as OUT type refcursor. Please help.
Thanks!
I have this piece of Code : p_rec is table type
DECLARE
p_rec PORTAL_LOAD_PKG.header_tab#test_link;
p_status VARCHAR2(200);
BEGIN
DASHBOARD_PRC#test_link('XYZ LIMITED', p_rec, p_status);
END;
Above code is working fine but my requirement is to create a procedure which returns the values of p_rec in refcursor.
Finally resolved.
First we have to create a Object and table type locally. Object definition should
be same as your record type which is declared on remote DB.
CREATE OR REPLACE TYPE dashboard_obj AS OBJECT (
i_num VARCHAR2(500),
desc VARCHAR2(1000),
od_num VARCHAR2(200),
i_amount NUMBER,
amount_paid NUMBER,
status VARCHAR2(200),
terms_date DATE,
i_id NUMBER,
v_id NUMBER
);
After that create table type of that object.
CREATE OR REPLACE TYPE tab1 IS
TABLE OF dashboard_obj;
Now Create procedure :
CREATE OR REPLACE PROCEDURE proc_test (
recordset OUT SYS_REFCURSOR
) IS
p_rec portal_load_pkg.header_tab#test_link;
p_status VARCHAR2(200);
obj dashboard_obj;
v_tab tab1 := tab1 ();
BEGIN
dashboard_prc#test_link ( 'XYZ LIMITED',p_rec,p_status );
FOR i IN 1.._rec.count LOOP
v_tab.extend;
v_tab(v_tab.count) := dashboard_obj(p_rec(i).i_num,
p_rec(i).desc,
p_rec(i).od_num,
p_rec(i).i_amount,
p_rec(i).amount_paid,
p_rec(i).status,
p_rec(i).terms_date,
p_rec(i).i_id,
p_rec(i).v_id
);
END LOOP;
OPEN recordset FOR SELECT
*
FROM
TABLE ( v_tab );
END;
If there is any other solution, please let me know. Thanks!

too many values error while using rowtype in procedure

CREATE OR replace PACKAGE emp_pkg
AS
PROCEDURE find_emp1(
c_id IN employees.employee_id%TYPE);
END emp_pkg;
/
CREATE OR replace PACKAGE BODY emp_pkg
AS
PROCEDURE Find_emp1(c_id IN employees.employee_id%TYPE)
IS
v_row employees%ROWTYPE;
BEGIN
SELECT first_name,
employee_id,
hire_date
INTO v_row
FROM employees
WHERE employee_id = c_id;
dbms_output.Put_line('ID:'
|| v_row.employee_id);
dbms_output.Put_line('NAME:'
||v_row.first_name);
dbms_output.Put_line('HIRE_DATE:'
|| v_row.hire_date);
END find_emp1;
END emp_pkg;
/
I think you have a mismatch between the 3 values in the select statement and the number of columns in your table. What are the table columns?

'Member of' in Oracle

I am trying to use member of in Oracle.
I am able to use this when table type is of number or any other data type. Below is the code for this:
declare
type t is table of number;
lt t;
begin
select channel_key
bulk collect into lt
from dim_channels;
if 22 member of lt then
dbms_output.put_line('ss');
end if;
end;
How do I use member of when the table is based on a record as in the code below.
declare
type rt is record
(
channel_key number(10),
channel_code varchar2(100)
);
type t is table of rt;
lt t;
lrt rt;
begin
select channel_key, channel_code
bulk collect into lt
from dim_channels;
end;
This won't work with plain local PL/SQL record types. To include more attributes you will need an object type with a MAP or ORDER function:
create or replace type demo_ot as object
( channel_key integer
, channel_code varchar2(30)
, map member function demo_map return varchar2 )
/
create or replace type body demo_ot as
map member function demo_map return varchar2
is
begin
return self.channel_key || '<#>' || self.channel_code;
end demo_map;
end;
/
declare
type demo_t is table of demo_ot; -- You would normally create this globally in SQL
my_set demo_t;
my_object demo_ot;
begin
select demo_ot(ckey, ccode)
bulk collect into my_set
from ( select 1 as ckey, 'One' as ccode from dual
union all
select 2 as ckey, 'Two' as ccode from dual );
my_object := demo_ot(2, 'Two');
if my_object member of my_set then
dbms_output.put_line('Member found');
else
dbms_output.put_line('Member not found');
end if;
end;
/
I created below code to test it
create or replace PROCEDURE P_MEMBER_OF_TEST(p_fname IN VARCHAR2,
p_lname in varchar2)
AS
type type_rec is record
(
first_name employees.first_name%type,
last_name employees.last_name%type
);
TYPE T_TAB_TYPE IS TABLE OF type_rec;
T_TAB T_TAB_TYPE;
t_rec type_rec;
i int;
BEGIN
t_rec.first_name := p_fname;
t_rec.last_name := p_lname;
SELECT FIRST_NAME,last_name bulk collect INTO T_TAB FROM
EMPLOYEES;
dbms_output.put_line(t_rec.first_name || ',' || t_rec.last_name);
IF t_rec MEMBER OF T_TAB THEN
DBMS_OUTPUT.PUT_LINE ('YES');
ELSE
DBMS_OUTPUT.PUT_LINE('NO');
END IF;
END;
It compiled with no issues however when i execute it i get error that my connection has been reset , when i comment the if-else-end if block . it gets executed. Can you also suggest what is the problem in code #William Robertson

Resources