select from table with function using pipelined table functions and associative table - oracle

I need to return the table as a result of this select. The argument passed to the function opredelyaet from which an array of associative data will be displayed as a table:
select * from table(task_2.get_con_coll('save'));
I wrote this code and to me it is correct and I do not see that I have missed, or that it is not paved with
Сreates an object
-- the creation of an array type
create or replace type con_coll_type is object(
id integer,
user_name varchar2(255));
Then he created the package
create or replace package task_2 is
/*
-- may need to be so?
type con_coll_type is record(
id integer,
user_name varchar(255));
-- */
--Create associative array
type con_coll_t is table of con_coll_type index by varchar2(255);
-- need using this
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
coll_edit con_coll_t;
coll_delete con_coll_t;
coll_save con_coll_t;
begin
-- Filling collection
coll_edit(1) := con_coll_type(1, 'some_name_1');
coll_edit(2) := con_coll_type(2, 'some_name_2');
coll_delete(3) := con_coll_type(3, 'some_name_3');
coll_delete(4) := con_coll_type(4, 'some_name_4');
coll_save(5) := con_coll_type(5, 'some_name_5');
coll_save(6) := con_coll_type(6, 'some_name_6');
-- If the parameter is passed to the function "Save" - ​​a collection of output
if coll_name = 'save' then
indx := coll_save.first;
loop
exit when indx is null;
-- pipelined output
pipe row(con_coll_type(coll_save(indx).id,
coll_save(indx).user_name));
indx := coll_save.next(indx);
end loop;
end if;
end get_con_coll;
end task_2;
What's wrong with my code? I can not understand what I missed.

First, the Table type should be out of the package:
create type con_coll_t is table of con_coll_type;
And, if you have default values for your types, you can implement them like that:
coll_delete con_coll_t;
begin
coll_delete := con_coll_t(con_coll_type(3, 'some_name_3'),
con_coll_type(4, 'some_name_4'));
end;
With that being said, I think your general code should be something like that:
-- the creation of an array type
create or replace type con_coll_type is object(
id integer,
user_name varchar2(255));
create or replace type con_coll_t is table of con_coll_type;
create or replace package task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
coll_edit con_coll_t;
coll_delete con_coll_t;
coll_save con_coll_t;
begin
-- Filling collection
coll_edit := con_coll_t( con_coll_type(1, 'some_name_1')
, con_coll_type(2, 'some_name_2'));
coll_delete := con_coll_t( con_coll_type(3, 'some_name_3')
, con_coll_type(4, 'some_name_4'));
coll_save := con_coll_t( con_coll_type(5, 'some_name_5')
, con_coll_type(6, 'some_name_6'));
-- If the parameter is passed to the function "Save" - ??a collection of output
if coll_name = 'save' then
indx := coll_save.first;
loop
exit when indx is null;
-- pipelined output
pipe row(con_coll_type(coll_save(indx).id,
coll_save(indx).user_name));
indx := coll_save.next(indx);
end loop;
end if;
end get_con_coll;
end task_2;

That's what I wanted and what made
create or replace type con_coll_type is object(id integer,
user_name varchar2(255));
create or replace type con_coll_t is table of con_coll_type;
create or replace package task_2 is
type list_of_oper is table of con_coll_t index by varchar2(255);
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined;
end task_2;
create or replace package body task_2 is
function get_con_coll(coll_name varchar2) return con_coll_t
pipelined is
indx varchar(255);
indx_2 varchar(255);
coll_main list_of_oper;
begin
coll_main('edit') := con_coll_t(con_coll_type(1, 'some_name_1'),
con_coll_type(2, 'some_name_2'));
coll_main('delete') := con_coll_t(con_coll_type(3, 'some_name_3'),
con_coll_type(4, 'some_name_4'));
coll_main('save') := con_coll_t(con_coll_type(5, 'some_name_5'),
con_coll_type(6, 'some_name_6'));
indx := coll_main.first;
loop
exit when indx is null;
if indx = coll_name then
indx_2 := coll_main(indx).first;
loop
exit when indx_2 is null;
pipe row(coll_main(indx)(indx_2));
/*pipe row(con_coll_type(coll_main(indx)(indx_2).id,
coll_main(indx)(indx_2).user_name));*/
indx_2 := coll_main(indx).next(indx_2);
end loop;
end if;
indx := coll_main.next(indx);
end loop;
return;
end get_con_coll;
end task_2;
select *
from table(task_2.get_con_coll('edit'))
union all
select *
from table(task_2.get_con_coll('delete'))
union all
select *
from table(task_2.get_con_coll('save'));

Related

Need help in the recursive procedure call

I have a requirement to write a procedure (that calls itself recursively).
The condition is like:
If the CONTACT NUMBER(assuming it as varchar2) STARTS WITH '100-%', then the procedure should print all the CONTACT NUMBERS that starts with 100 for the given group.
If CONTACT NUMBER starts with '50-%' then it should call recursively.
I have tried writing a sample procedure, but unfortunately not getting the result.
CREATE OR REPLACE TYPE type_t AS
TABLE OF VARCHAR2(100);
CREATE OR REPLACE PROCEDURE proc_test (
in_group IN VARCHAR2,
contact_nmbr OUT VARCHAR2
) AS
v_out type_t := type_t ();
BEGIN
SELECT
contact_id
BULK COLLECT INTO
v_out
FROM
my_table
WHERE
group_id = in_group;
FOR i IN 1..v_out.count LOOP
v_out.extend;
IF
v_out(i) LIKE '100-%'
THEN
contact_nmbr := v_out(i);
ELSIF v_out(i) LIKE '50-%' THEN
proc_test(v_out(i),contact_nmbr);
END IF;
END LOOP;
END;
/
I am not getting the output after running this proc.
DECLARE
in_group VARCHAR2(30) := '123ABC';
contact_nmbr VARCHAR2(30);
BEGIN
proc_test(in_group,contact_nmbr);
dbms_output.put_line(contact_nmbr);
END;
/
This is the sample data in table MY_TABLE
Group_Id Contact_Id
---------------------------------------
001 100-001-01
001 70-001-01
001 100-002-01
001 50-001-01
50-001-01 30-001-01
50-001-01 100-100-01
50-001-01 50-100-01
50-100-01 50-200-01
Couple of issues I can see with your script....
1) You are declaring the in_group to = 123ABC there is no data matching in your sample data so it will never return anything...
in_group VARCHAR2(30) := '123ABC';
2) You do not exit the loop when you successfully find a contact number so unless your last record is a match you will get no output;
3) when you increment the v_out collection on each loop this serves no purpose this does not do any harm but is not necassary
v_out.extend;
So remove the extend and add a exit as below....
CREATE OR REPLACE PROCEDURE proc_test (
in_group IN VARCHAR2,
contact_nmbr OUT VARCHAR2
) AS
v_out type_t := type_t ();
BEGIN
SELECT
contact_id
BULK COLLECT INTO
v_out
FROM
my_table
WHERE
group_id = in_group;
FOR i IN 1..v_out.count LOOP
IF
v_out(i) LIKE '100-%'
THEN
contact_nmbr := v_out(i);
exit;
ELSIF v_out(i) LIKE '50-%' THEN
proc_test(v_out(i),contact_nmbr);
END IF;
END LOOP;
END;
and call with a valid id as below and you should be good to go.
DECLARE
in_group VARCHAR2(30) := '001';
contact_nmbr VARCHAR2(30);
BEGIN
proc_test(in_group,contact_nmbr);
dbms_output.put_line(contact_nmbr);
END;

PLSQL: Assign array returned by one function into an array defined in another function

I have two functions – func1 and func2. func1 selects some values from a table and assigns it to an array and returns this array. func2 calls func1. func2 uses the array returned by func1 to perform some operations. My question is: How to assign the array returned by func1 to an array in func2. Please find below the code snippets of func1 and func2.
function func1 (table1 varchar2, table2 varchar2) return j_list
is
type j_list is varray (10) of VARCHAR2(50);
attr_list j_list := j_list();
counter integer :=0;
begin
for i in
(select a.column_name from all_tab_columns a)
LOOP
counter := counter + 1;
attr_list.extend;
attr_list(counter) := i.column_name;
END LOOP;
return attr_list;
end func1;
function func2 (table1 varchar2, table2 varchar2) return varchar2
is
type new_j_list is varray (10) of VARCHAR2(50);
new_attr_list j_list := j_list();
new_attr_list.extend;
new_attr_list() := func1 (table1, table2) /*does this assign the array
that is returned by func1 into the array new_attr_list ??? */
jt varchar2(4000);
begin
jt := /*some operations using the new_attr_list*/
return jt;
end func2;
Your code have some errors. You cannot use j_list without declaring it at first place. The scope of j_list has to be declared prior to use. See below how you can do it. Also method extend should be used in begin block not in declaration block.
CREATE OR REPLACE TYPE j_list IS VARRAY (10) OF VARCHAR2(50);
/
CREATE OR REPLACE FUNCTION func1 ( table1 VARCHAR2,
table2 VARCHAR2
)
RETURN j_list
IS
attr_list j_list := j_list ();
counter INTEGER := 0;
BEGIN
FOR i IN ( SELECT a.column_name FROM all_tab_columns a)
LOOP
counter := counter + 1;
attr_list.extend;
attr_list(counter) := i.column_name;
END LOOP;
RETURN attr_list;
END func1;
/
CREATE OR REPLACE FUNCTION func2 ( table1 VARCHAR2,
table2 VARCHAR2
) RETURN VARCHAR2
IS
new_attr_list j_list := j_list ();
jt VARCHAR2(4000);
BEGIN
new_attr_list.extend;
new_attr_list:= func1(table1, table2 );
jt := 'some operations using the new_attr_list';
RETURN jt;
END func2;

'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

Procedure to update a table which is already populated

I want to create a procedure that adds new rows to an already existing table.But with the current procedure I've, I'm rewriting the entire table. The code for the current procedure is
CREATE TYPE t_tf_row AS OBJECT (
id NUMBER,
description VARCHAR2(50));
CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
create or replace procedure add_n_rows(
n_rows in number)
is
l_tab t_tf_tab := t_tf_tab();
begin
for i in l_tab.count .. l_tab.count + n_rows
loop
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
Here, everytime I'm rewriting the entire l_tab. I want to update the one which is already updated. Suggest me the right method for the required procedure.Thanks
This is because you're re-creating the object. You need to pass an instantiated version of the object into the procedure as a parameter:
create or replace procedure add_n_rows(
Pn_rows in number
, P_tab in out t_tf_tab ) is
begin
for i in P_tab.count .. P_tab.count + Pn_rows
loop
P_tab.extend;
P_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
I've declared P_tab as an OUT parameter, this means you can alter it. If you don't want to do this then remove "out" and declare a local variable of the type t_tf_tab, which you can then alter.
You can then call it separately, for instance:
declare
l_tab t_tf_tab := t_tf_tab();
begin
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(1. 'Hello');
add_n_rows(3, l_tab);
end;

Using Tables of Records in PL/SQL

I've declared the following types in my PL/SQL package:
TYPE t_simple_object IS RECORD (
wert NUMBER,
gs NUMBER,
vl NUMBER);
TYPE t_obj_table IS TABLE OF t_simple_object
INDEX BY BINARY_INTEGER;
Then I declare a variable:
obj t_obj_table;
However, when I want to use the variable, I cannot initialize or extend it:
obj := t_obj_table ();
gives the following errror:
PLS-00222: no function with name 'T_OBJ_TABLE' exists in this scope
If I don't initialize it, I can't extend it to add some date as
obj.EXTEND();
gives another error:
PLS-00306: wrong number or types of arguments in call to 'EXTEND'
How can I make this work?
You don't extend a table indexed by "something", you can just use it...
DECLARE
TYPE t_simple_object IS RECORD
( wert NUMBER
, gs NUMBER
, vl NUMBER
);
TYPE t_obj_table IS TABLE OF t_simple_object
INDEX BY BINARY_INTEGER;
my_rec t_simple_object;
obj t_obj_table;
BEGIN
my_rec.wert := 1;
my_rec.gs := 1;
my_rec.vl := 1;
obj(1) := my_rec;
END;
/
To use the EXTEND syntax, this example should do it...
DECLARE
TYPE t_simple_object IS RECORD
( wert NUMBER
, gs NUMBER
, vl NUMBER
);
TYPE t_obj_table IS TABLE OF t_simple_object;
my_rec t_simple_object;
obj t_obj_table := t_obj_table();
BEGIN
obj.EXTEND;
my_rec.wert := 1;
my_rec.gs := 1;
my_rec.vl := 1;
obj(1) := my_rec;
END;
/
Also see this link (Ask Tom)
If you don't want to use an associative array (aka index-by table) then leave out the "INDEX BY BINARY_INTEGER" clause. Your code then works OK:
declare
TYPE t_simple_object IS RECORD (
wert NUMBER,
gs NUMBER,
vl NUMBER);
TYPE t_obj_table IS TABLE OF t_simple_object;
obj t_obj_table;
begin
obj := t_obj_table ();
obj.EXTEND();
end;
You can not extend an assosiative array.
Just assign values to it
declare
TYPE t_simple_object IS RECORD (
wert NUMBER,
gs NUMBER,
vl NUMBER);
TYPE t_obj_table IS TABLE OF t_simple_object INDEX BY BINARY_INTEGER;
simple_object t_simple_object;
begin
simple_object.wert := 1;
simple_object.gs := 2;
simple_object.vl := 3;
obj(1) := simple_object;
end;
/
Or just use a record ( or associate array of records )
create or replace package p_test is
type t_rec is record (
empname varchar2(50),
empaddr varchar2(50));
function p_test_ret_record return t_rec;
end p_test;
create or replace package body p_test is
function p_test_ret_record return t_rec is
l_rec t_rec;
begin
l_rec.empname := 'P1';
l_rec.empaddr := 'P2';
return l_rec;
end;
end p_test;
declare
-- Non-scalar parameters require additional processing
result p_test.t_rec;
begin
-- Call the function
result := p_test.p_test_ret_record;
dbms_output.put_line('Name: ' || result.empname || ' Addr: ' || result.empaddr);
end;

Resources