User-defined record as an OUT parameter - oracle

This is my current code. My error is that COUNTRY_REC must be declared. I understand all of that I just can't find anything online that talks about records as out parameters. This is a requirement of the problem. I have to be very close to the solution but I just don't understand.
CREATE OR REPLACE PROCEDURE find_region_and_currency_sp(
p_country_name IN COUNTRIES.COUNTRY_NAME%TYPE,
p_country_rec OUT country_rec)
IS
TYPE country_rec IS RECORD(
country_name COUNTRIES.COUNTRY_NAME%TYPE,
region COUNTRIES.REGION_ID%TYPE,
currency COUNTRIES.CURRENCY_CODE%TYPE);
country_record country_rec;
BEGIN
SELECT COUNTRY_NAME, REGION_ID, CURRENCY_CODE INTO country_rec
FROM COUNTRIES
where COUNTRY_NAME = p_country_name;
END;
Any help would be greatly appreciated...

Your record TYPE COUNTRY_REC is defined inside procedure which is not accessible within scope of procedure parameters.
You have the following options.
Create and store a TYPE OBJECT COUNTRY_REC with same structure as that of the query result and compatible datatypes.
CREATE OR REPLACE TYPE country_rec AS OBJECT (
country_name VARCHAR2(10),
region VARCHAR2(10),
currency VARCHAR2(5)
);
You then don't need another local record variable.But, you need to initialize using NEW keyword as shown. You can then simply fetch the values into corresponding record elements.
CREATE OR REPLACE PROCEDURE find_region_and_currency_sp (
p_country_name IN countries.country_name%TYPE,
p_country_rec OUT country_rec
)
IS
BEGIN
p_country_rec := NEW country_rec(NULL,NULL,NULL); --Initialization
SELECT
country_name,
region_id,
currency_code
INTO
p_country_rec.country_name,p_country_rec.region,p_country_rec.currency
FROM
countries
WHERE
country_name = p_country_name;
END;
The other Option is to define the record in the package scope in the package specification.
CREATE OR REPLACE PACKAGE pkg_country_op
AS
TYPE country_rec IS RECORD(
country_name COUNTRIES.COUNTRY_NAME%TYPE,
region COUNTRIES.REGION_ID%TYPE,
currency COUNTRIES.CURRENCY_CODE%TYPE);
PROCEDURE find_region_and_currency_sp (
p_country_name IN COUNTRIES.country_name%TYPE,
p_country_rec OUT country_rec
);
END;
/
And then include the same procedure inside the PACKAGE BODY.
CREATE OR REPLACE PACKAGE BODY pkg_country_op..
..
..
PROCEDURE find_region_and_currency_sp (
p_country_name IN countries.country_name%TYPE,
p_country_rec OUT country_rec
)
IS
..
Calling the procedure. You can call the procedure as follows.
Case 1:
DECLARE
v_country_rec country_rec; --Global Scope
BEGIN
find_region_and_currency_sp('India',v_country_rec);
END;
/
Case 2:
DECLARE
v_country_rec pkg_country_op.country_rec; --Package Global variable
BEGIN
pkg_country_op.find_region_and_currency_sp('India',v_country_rec);
END;
/

Related

Method call ends with error ORA-04063 + ORA-06512

I have such objects and methods:
create or replace type PolicyType as object(
policy_id number,
policy_nr varchar2(20),
price number) NOT FINAL;
/
create or replace type TravelerType as object(
first_name varchar2(30),
last_name varchar2(30),
pers_code varchar2(12));
/
create or replace type TravelersType as table of TravelerType;
/
create or replace type ref_TargetPlaceType as object (
target_code varchar2(4),
description varchar2(30));
/
create or replace type TravelPolicyType under PolicyType (
target_code REF ref_TargetPlaceType,
traveler_data TravelersType,
MEMBER FUNCTION getTraveler (p_code IN varchar2) RETURN TravelerType,
MEMBER PROCEDURE addTraveler( traveler IN TravelerType ));
/
CREATE OR REPLACE TYPE BODY TravelPolicyType AS
MEMBER FUNCTION getTraveler(p_code IN varchar2) RETURN TravelerType AS
BEGIN
for i in 1 .. self.traveler_data.count() loop
if( self.traveler_data(i).pers_code = p_code )
then
return self.traveler_data(i);
end if;
end loop;
END;
MEMBER PROCEDURE addTraveler(traveler IN TravelerType) AS
BEGIN
self.traveler_data.extend();
self.traveler_data(self.traveler_data.count) :=traveler;
END;
END;
I am tying to call methods, bet getting error for both
ORA-04063: type body "SQL_LPZCXWDSKRAXJUEWPMXNUMWYH.TRAVELPOLICYTYPE" has errors ORA-06512:
declare
policy_d TravelPolicyType;
traveler TravelerType;
begin
select value(a) into policy_d from travel_policies a where a.policy_nr='TP00000000003';
traveler:=policy_d.getTraveler('050976-12568');
end;
declare
policy_d TravelPolicyType;
new_traveler TravelerType;
begin
select value(a) into policy_d from travel_policies a where a.policy_nr='TP00000000003';
new_traveler:=TravelerType('Test','Traveler', '123456-44444');
policy_d.addTraveler(new_traveler);
end;
Can not understand what I am doing wrongly. Please help with advise
See what errors are reported in that object:
select * from user_errors;
NAME
TYPE
SEQUENCE
LINE
POSITION
TEXT
ATTRIBUTE
MESSAGE_NUMBER
TRAVELPOLICYTYPE
TYPE BODY
1
4
22
PLS-00302: component 'TRAVELERS_DATA' must be declared
ERROR
302
TRAVELPOLICYTYPE
TYPE BODY
2
4
3
PL/SQL: Statement ignored
ERROR
0
Your type has traveler_data, but the body refers to travelers_data. You need to make that naming consistent.

Define custom type in package

I have the below type:
create or replace TYPE t3_data AS OBJECT (
vk NVARCHAR2(12),
pl VARCHAR2(2000),
dt NUMBER,
ay CHAR(10),
ao CHAR(10),
an VARCHAR2(4000),
r NUMBER,
c CHAR(10)
);
Which I use in a function like the below:
FUNCTION pick_values RETURN t3_table PIPELINED
The relation between t3_data and t3_table is the below:
create or replace TYPE t3_table IS
TABLE OF t3_data;
Is there a way to define t3_data and t3_table inside a package in which I already have the function defined?
Object types are SQL types so they cannot be c reated in PL/SQL. You can, however, define a record type in a package rather than an object type and you should be able to use that record type to define a pipelined table function
create or replace package test_pkg
as
type t3_data_rec is record (
vk NVARCHAR2(12),
pl VARCHAR2(2000),
dt NUMBER,
ay CHAR(10),
ao CHAR(10),
an VARCHAR2(4000),
r NUMBER,
c CHAR(10)
);
type t3_table is table of t3_data_rec;
function pick_values
return t3_table pipelined;
end;
/
create or replace package body test_pkg
as
function pick_values
return t3_table pipelined
is
l_rec t3_data_rec;
begin
l_rec.vk := N'foo';
pipe row( l_rec );
end;
end;
/
select *
from table( test_pkg.pick_values );

Passing a table of varchar2 to a procedure

I need have a package with procedure accepting a parameter of type table of varchar2(255).
Package spec:
type charArray is table of varchar2(255) index by binary_integer;
Package body:
PROCEDURE call_me (p1 IN charArray, p2 IN VARCHAR2) IS ...
I tried calling this procedure using:
declare
type t_type is table of varchar2(255) index by binary_integer;
l_array t_type;
begin
l_array := ('131240');
pkg._name.call_me (l_array, 'name');
end;
I'm getting this error:
PLS-00306: wrong number or types of arguments in call to 'call_me'
What am I doing wrong?
You've got two types declared with the same definition, but that doesn't make them compatible. In order to pass the table into the package, you must use the type declared in the package:
declare
l_array pkg_name.charArray;
begin
l_array(1) := '131240';
pkg_name.call_me (l_array, 'name');
end;

create a collection with 2 columns and insert all the rows at once PL/SQL

I have reference data which I want to use in a PL/SQL package.
This is a collection with two columns:
type table_info IS RECORD (
table_name VARCHAR2(50),
join_column VARCHAR2(50)
);
type config_tables_type is table of table_info; -- list of the config tables
I would like to add several rows at once to this collection, I tried this:
config_tables config_tables_type := config_tables_type (table_info('Commands','object_id'),
table_info('Contact_notificationcommands','command_object_id'),
table_info('Contactgroup_members','contact_object_id'),
table_info('Contactgroups','contact_object_id'),
table_info('Contactnotificationmethods','command_object_id'),
table_info('customvariables','object_id'),
table_info('Host_contactgroups','host_id'),
table_info('Host_contacts','host_id'),
table_info('Hostescalation_contactgroups','contactgroup_object_id'),
table_info('Hostescalation_contacts','contact_object_id'),
table_info('Host_parenthosts','parent_host_object_id'),
table_info('Hostdependencies','host_object_id'),
table_info('Hostdependencies','dependent_host_object_id'),
table_info('Hostescalations','host_object_id'),
table_info('Hostgroup_members','host_object_id'),
table_info('Hostgroups','hostgroup_object_id'),
table_info('Hosts','host_object_id'),
table_info('Service_contactgroups','contactgroup_object_id'),
table_info('Service_contacts','contact_object_id'),
table_info('Servicedependencies','service_object_id'),
table_info('Serviceescalation_contactgroups','contactgroup_object_id'),
table_info('Serviceescalation_contacts','contact_object_id'),
table_info('Serviceescalations','service_object_id'),
table_info('Servicegroup_members','service_object_id'),
table_info('Servicegroups','servicegroup_object_id'),
table_info('Services','service_object_id'),
table_info('Timeperiods','timeperiod_object_id')
);
But I have the following compilation error:
PLS-00222: no function with name 'TABLE_INFO' exists in this scope.
This type is declared in the package description and the initialisation of the config_tables collection is done in the package body.
Thanks
The below "hack" should do the trick!
declare
type table_info IS RECORD (
table_name VARCHAR2(50),
join_column VARCHAR2(50)
);
type config_tables_type is table of table_info;
config_tables config_tables_type;
function table_info_constructor(table_name VARCHAR2, join_column VARCHAR2) return table_info
is
t_i table_info;
begin
t_i.table_name := table_name;
t_i.join_column := join_column;
return(t_i);
end;
begin
config_tables := config_tables_type(table_info_constructor('Commands','object_id'),
table_info_constructor('Contact_notificationcommands','command_object_id'),
table_info_constructor('Contactgroup_members','contact_object_id'),
table_info_constructor('Contactgroups','contact_object_id'),
table_info_constructor('Contactnotificationmethods','command_object_id'),
table_info_constructor('customvariables','object_id'),
table_info_constructor('Host_contactgroups','host_id'),
table_info_constructor('Host_contacts','host_id'),
table_info_constructor('Hostescalation_contactgroups','contactgroup_object_id'),
table_info_constructor('Hostescalation_contacts','contact_object_id'),
table_info_constructor('Host_parenthosts','parent_host_object_id'),
table_info_constructor('Hostdependencies','host_object_id'),
table_info_constructor('Hostdependencies','dependent_host_object_id'),
table_info_constructor('Hostescalations','host_object_id'),
table_info_constructor('Hostgroup_members','host_object_id'),
table_info_constructor('Hostgroups','hostgroup_object_id'),
table_info_constructor('Hosts','host_object_id'),
table_info_constructor('Service_contactgroups','contactgroup_object_id'),
table_info_constructor('Service_contacts','contact_object_id'),
table_info_constructor('Servicedependencies','service_object_id'),
table_info_constructor('Serviceescalation_contactgroups','contactgroup_object_id'),
table_info_constructor('Serviceescalation_contacts','contact_object_id'),
table_info_constructor('Serviceescalations','service_object_id'),
table_info_constructor('Servicegroup_members','service_object_id'),
table_info_constructor('Servicegroups','servicegroup_object_id'),
table_info_constructor('Services','service_object_id'),
table_info_constructor('Timeperiods','timeperiod_object_id')
);
end;
Let me know how it works out for you.
For any further clarifications don't hesitate to ask me.
Ted.
There is an another alternative where you can basically create a schema level object i.e OBJECT type and TABLE Type and then call it in the plsql block as shown below. Hope this helps too.
--Create object type
CREATE OR REPLACE TYPE table_info
IS
OBJECT
(
table_name VARCHAR2(50),
join_column VARCHAR2(50))
/
--Create table type on Object type
CREATE OR REPLACE TYPE config_tables_type
IS
TABLE OF table_info
/
--PLSQL block of code
DECLARE
config_tables config_tables_type;
BEGIN
config_tables config_tables_type := config_tables_type (table_info('Commands','object_id'),
table_info('Contact_notificationcommands','command_object_id'),
table_info('Contactgroup_members','contact_object_id'),
table_info('Contactgroups','contact_object_id'),
table_info('Contactnotificationmethods','command_object_id'),
table_info('customvariables','object_id'),
table_info('Host_contactgroups','host_id'),
table_info('Host_contacts','host_id'),
table_info('Hostescalation_contactgroups','contactgroup_object_id'),
table_info('Hostescalation_contacts','contact_object_id'),
table_info('Host_parenthosts','parent_host_object_id'),
table_info('Hostdependencies','host_object_id'),
table_info('Hostdependencies','dependent_host_object_id'),
table_info('Hostescalations','host_object_id'),
table_info('Hostgroup_members','host_object_id'),
table_info('Hostgroups','hostgroup_object_id'),
table_info('Hosts','host_object_id'),
table_info('Service_contactgroups','contactgroup_object_id'),
table_info('Service_contacts','contact_object_id'),
table_info('Servicedependencies','service_object_id'),
table_info('Serviceescalation_contactgroups','contactgroup_object_id'),
table_info('Serviceescalation_contacts','contact_object_id'),
table_info('Serviceescalations','service_object_id'),
table_info('Servicegroup_members','service_object_id'),
table_info('Servicegroups','servicegroup_object_id'),
table_info('Services','service_object_id'),
table_info('Timeperiods','timeperiod_object_id')
);
END;
/

Can we use a table type parameter as a default null parameter in PLSQL?

I have a record type as follows,
TYPE x_Rec IS RECORD(
master_company x_tab.master_company%TYPE,
report_trans_type x_tab.report_trans_type%TYPE,
balance_version_id x_tab.balance_version_id%TYPE,
reporting_entity x_tab.reporting_entity%TYPE,
year_period_from x_tab.year_period%TYPE,
year_period_to x_tab.year_period%TYPE,
journal_id x_tab.journal_id%TYPE,
row_id x_tab.row_id%TYPE);
and I have created a table type using this record:
TYPE x_rec_tab IS TABLE OF x_Rec INDEX BY PLS_INTEGER;
I want to use this table type in a procedure as a default null parameter.
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default null)
IS
BEGIN
...My code
END;
It gives the following error message
PLS-00382: expression is of the wrong type
I resolved this by using CAST(null as /*your_type*/) in the Procedure's signature.
For instance, in your case, it will be something like this:
PROCEDURE x_Balance (x_param IN NUMBER,
x_rec_ IN x_rec_tab default cast(null as x_rec_tab))
Then, within the procedure, you just need to check if x_rec_ has elements by using the count method.
This way works for me.
You can't do that with an associative array, as that can never be null. You would get the same error if you tried to assign null to a variable of type x_rec_tab. They also don't have constructors, so you can't use an empty collection instead.
You can do this will a varray or more usefully for your situation a nested table:
create or replace package p42 as
TYPE x_Rec IS RECORD(
master_company x_tab.master_company%TYPE,
report_trans_type x_tab.report_trans_type%TYPE,
balance_version_id x_tab.balance_version_id%TYPE,
reporting_entity x_tab.reporting_entity%TYPE,
year_period_from x_tab.year_period%TYPE,
year_period_to x_tab.year_period%TYPE,
journal_id x_tab.journal_id%TYPE,
row_id x_tab.row_id%TYPE);
-- no index-by clause, so nested table not associative array
TYPE x_rec_tab IS TABLE OF x_Rec;
end p42;
/
Package P42 compiled
show errors
No errors.
create or replace package body p42 as
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default null)
IS
BEGIN
--...My code
null;
END;
PROCEDURE dummy IS
l_rec_tab x_rec_tab;
BEGIN
l_rec_tab := null;
END;
end p42;
/
Package Body P42 compiled
show errors;
No errors.
You could also default to an empty collection instead:
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default x_rec_tab())
IS
...
That doesn't really help you much if you have other code that relies on the type being an associative array of course.
Old question but still might be helpful.
You can create a function:
function empty_tab
return x_rec_tab
as
l_tab x_rec_tab;
begin
return l_tab;
end empty_tab;
This way you can (notice that empty_tab is used as default parameter):
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default empty_tab)
IS
BEGIN
...My code
END;
This is a repeat of #ManuelPerez answer, but I just feel that it could have been explained better.
Create this procedure, casting your optional variable to your datatype like this:
CREATE OR REPLACE PROCEDURE Test_Procedure (
txt_ IN VARCHAR2,
col_formats_ IN dbms_sql.varchar2a DEFAULT cast(null as dbms_sql.varchar2a) )
IS BEGIN
Dbms_Output.Put_Line (txt_);
FOR i_ IN 1 .. 10 LOOP
IF col_formats_.EXISTS(i_) THEN
Dbms_Output.Put_Line (i_ || ' Exists');
ELSE
Dbms_Output.Put_Line (i_ || ' DOES NOT Exist');
END IF;
END LOOP;
END Test_Procedure;
The reason this beats the accepted answer is that it doesn't require you to change the datatype of the incoming variable. Depending on your circumstance, you may not have the flexibility to do that.
Now call your procedure like this if you have a variable to feed the procedure:
DECLARE
txt_ VARCHAR2(100) := 'dummy';
arr_ dbms_sql.varchar2a;
BEGIN
arr_(4) := 'another dummy';
Test_Procedure (txt_, arr_);
END;
Or like this if you don't:
DECLARE
txt_ VARCHAR2(100) := 'dummy';
BEGIN
Test_Procedure (txt_);
END;
Your output will look something like this:
dummy
1 DOES NOT Exist
2 DOES NOT Exist
3 DOES NOT Exist
4 Exists
5 DOES NOT Exist
6 DOES NOT Exist
7 DOES NOT Exist
8 DOES NOT Exist
9 DOES NOT Exist
10 DOES NOT Exist

Resources