Using Tables of Records in PL/SQL - oracle

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;

Related

Initialize a PLSQL record type initialize a nested table of record type

I am using Oracle 11g
getting error
no function with name 'T_TAB' exists in this scope
declare
TYPE t_tab IS RECORD
(
col1 number,
col2 number
);
TYPE t_tab_arr IS TABLE OF t_tab;
l_tab1 t_tab_arr := t_tab_arr (t_tab(1,2),t_tab(2,3));
BEGIN
NULL;
END;
/
In Oracle 18, your code works (db<>fiddle).
In Oracle 11g, there is no constructor method for PL/SQL RECORD data types (like there is for SQL OBJECT data types); instead, you need to declare the RECORD values and assign values to their fields before putting them into the collection:
DECLARE
TYPE t_tab IS RECORD(
col1 number,
col2 number
);
TYPE t_tab_arr IS TABLE OF t_tab;
l_t1 T_TAB;
l_t2 T_TAB;
l_tab1 T_TAB_ARR;
BEGIN
l_t1.col1 := 1;
l_t1.col2 := 2;
l_t2.col1 := 3;
l_t2.col2 := 4;
l_tab1 := T_TAB_ARR( l_t1, l_t2 );
FOR i IN 1 .. l_tab1.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( i || ': (' || l_tab1(i).col1 || ', ' || l_tab1(i).col2 ||')' );
END LOOP;
END;
/
Which outputs:
1: (1, 2)
2: (3, 4)
db<>fiddle here

associate array constructor not found when compiling a stored procedure

When using a type that is already defined in a package as
Type ArrError Is Table Of Varchar2(20) Index By Binary_Integer;
and declaring it as following in a stored procedure
arr_error(1) := PKG_TEST.arrerror(p_error);
results in the following error PLS-00222: no function with name 'ARRERROR' exists in this scope. In the above statement p_error is of type VARCHAR2
When using a global type created using CREATE TYPE command this works fine.
using the above initialization in a CREATE PROCEDURE block.
You don't need to initialize if you got Index by ...
declare
TYPE ArrError IS TABLE OF VARCHAR2 (20) index by binary_integer;
arr_error ArrError;
p_error varchar2(20) := 'test';
p_error2 varchar2(20) := 'test2';
begin
arr_error(1) := p_error;
arr_error(2) := p_error2;
dbms_output.put_line('arr_error(1): ' || arr_error(1));
dbms_output.put_line('arr_error(2): ' || arr_error(2));
end;
If you don't have index by ... you need to call the constructor:
declare
TYPE ArrError IS TABLE OF VARCHAR2 (20);
arr_error ArrError;
p_error varchar2(20) := 'test';
p_error2 varchar2(20) := 'test2';
begin
-- Needed because of no 'Indexed by'. Or else you get a 'ORA-06531' not initialized..
arr_error := ArrError(p_error);
arr_error.extend;
arr_error(2) := p_error2;
dbms_output.put_line('arr_error(1): ' || arr_error(1));
dbms_output.put_line('arr_error(2): ' || arr_error(2));
end;

How to call and execute a Oracle stored procedure using TYPE as a parameter

This is the package specification:
PROCEDURE Save_Countries
( p_inst_id Number,
p_CountryList IN T_COUNTRY_TYPE
)
AS
v_count number;
BEGIN
for i in p_CountryList.first..p_CountryList.last loop
insert into countries (INST_ID, COUNTRY_NAME, COUNTRY_CODE)
values (p_inst_id, p_CountryList(i).COUNTRY_NAME, p_CountryList(i).COUNTRY_CODE);
end loop;
Commit;
end Save_Countries ;
This is the Type specification:
create or replace TYPE OBJ_COUNTRY FORCE as OBJECT (
COUNTRY_ID NUMBER(10),
COUNTRY_NAME VARCHAR2(255),
COUNTRY_CODE VARCHAR2(6)
);
create or replace TYPE T_COUNTRY_TYPE as TABLE OF OBJ_COUNTRY;
So far I have this:
DECLARE
P_INST_ID NUMBER;
P_COUNTRYLIST T_COUNTRY_TYPE;
BEGIN
P_INST_ID := 255;
PKG_TEST.SAVE_COUNTRIES(
P_INST_ID => P_INST_ID,
P_COUNTRYLIST => P_COUNTRYLIST
);
END;
Question
I am trying to call the store procedure from my Oracle SQL Developer. How I can initialize the list (TYPE) so I can pass that as a parameter. I've googled around but I wasn't able to find something to help with this.
For nested tables, you must initialise and assign elements to the collection.
DECLARE
P_INST_ID NUMBER;
P_COUNTRYLIST T_COUNTRY_TYPE := T_COUNTRY_TYPE(); --initialization
BEGIN
P_INST_ID := 255;
P_COUNTRYLIST.extend(2); --allocate 2 null elements.
P_COUNTRYLIST(1) := OBJ_COUNTRY(1,'INDIA','IND'); --set the values
P_COUNTRYLIST(2) := OBJ_COUNTRY(2,'AUSTRALIA','AUS');
--To assign n more elements, use P_COUNTRYLIST.extend(n);
PKG_TEST.SAVE_COUNTRIES(
P_INST_ID => P_INST_ID,
P_COUNTRYLIST => P_COUNTRYLIST
);
END;
/
Demo

how to pass object type as a parameter in oracle

below abstarct type created
create or replace TYPE callbck as table of callback_t;
abstract table
create or replace TYPE CALLBACK_T as object
(
url varchar2(50),
uri_key number,
);
below is the procedure used,
procedure get_callback_info
(
pi_clbk callbck := callbck (),
requestor varchar2,
msg varchar2)
how to pass the parameter for the abstract object type to the procedure get_callback_info.
i have the abstract values 'http://test.com', and 1345 as the url and uri_key
Say you have these types and procedure:
CREATE OR REPLACE TYPE CALLBACK_T AS OBJECT
(
url VARCHAR2(50),
uri_key NUMBER
);
CREATE OR REPLACE TYPE callbck AS TABLE OF callback_t;
CREATE OR REPLACE PROCEDURE get_callback_info(
pi_clbk IN callbck := callbck(),
requestor IN VARCHAR2,
msg OUT VARCHAR2
) IS
BEGIN
/* whatever your code is */
msg := requestor || ' asked information on callbck with ' || pi_clbk.COUNT || ' elements';
END;
You can test your procedure with something like:
declare
vCallbck callbck;
vRequestor varchar2(20) := 'a requestor';
vMsg varchar2(100);
begin
/* populate the vCallbck with 10 records */
select CALLBACK_T ('url ' || level, level)
bulk collect into vCallbck
from dual
connect by level <= 10;
--
get_callback_info(vCallbck, vRequestor, vMsg);
--
dbms_output.put_line(vMsg);
end;
/
If you want to test with a single value, you can use:
declare
vCallbck callbck;
vRequestor varchar2(20);
vMsg varchar2(100);
begin
vCallbck := callbck();
vCallbck.extend(1);
vCallbck(1) := CALLBACK_T('http://test.com', '1345');
--
get_callback_info(vCallbck, vRequestor, vMsg);
--
dbms_output.put_line(vMsg);
end;
/

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

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'));

Resources