I am having issues in printing the custom object.
Here is my complete use case
create or replace type invoice_obt
as object (
invoice_id number
);
/
create type invoices_ntt
as table of invoice_obt;
/
create type customer_with_invoices
as object (
customer_id number,
invoices invoices_ntt
)
declare
l_customer customer_with_invoices;
l_invoices invoices_ntt := invoices_ntt();
begin
l_invoices.extend(3);
l_invoices(1) := invoice_obt(100);
l_invoices(2) := invoice_obt(200);
l_invoices(3) := invoice_obt(200);
l_customer := customer_with_invoices(1,l_invoices);
end;
How do i print out the l_customer using dbms_output please.
When you call customer_with_invoices(), you want to call the constructor, so you need to pass parameters for the object properties, even null if you want.
Also, the extend applies to l_customers.invoices, and not to l_customers.
For example, this works:
declare
l_customers customer_with_invoices := customer_with_invoices(null, null);
begin
l_customers.invoices := invoices_ntt();
l_customers.invoices.extend(1);
l_customers.invoices(1) := new invoice_obt(1);
--
dbms_output.put_line(l_customers.invoices(1).invoice_id);
end;
/
To print the content of the object with dbms_output.put_line , you need to pass it the elements, given that it does not handle objects. In your edited code, this is an example:
for i in 1 .. l_customer.invoices.count loop
dbms_output.put_line('invoice (' || i || ') = ' || l_customer.invoices(i).invoice_id);
end loop;
In a production code, you should better check if objects exist, before trying to use .count or similar.
Related
I have the following code which works very nice:
declare
v_order_id oe.orders.order_id%type := 1;
v_order_item pkg_order_management.to_order_list := pkg_order_management.to_order_list();
begin
v_order_item.extend(2);
v_order_item(1).product_id := 2289;
v_order_item(1).quantity := 2;
v_order_item(2).product_id := 2058;
v_order_item(2).quantity := 5;
pkg_order_management.prc_create_order(240, v_order_item, v_order_id);
dbms_output.put_line('it was created the order: ' || v_order_id);
end;
but I want to call the pkg_order_management.prc_create_order procedure like
declare
v_order_id oe.orders.order_id%type := 1;
begin
pkg_order_management.prc_create_order(240, ((2289, 2),(2058, 5)), v_order_id);
dbms_output.put_line('it was created the order: ' || v_order_id);
end;
Here is the types definition from the package:
type t_order_item is record
( product_id oe.order_items.product_id%type
, quantity oe.order_items.quantity%type);
type to_order_list is table of t_order_item;
When I call the procedure as in 2nd case, I receive the following error:
PLS-00306: wrong number or types of arguments in call to
'PRC_CREATE_ORDER'
Surely, my call type is wrong but I have no idea how to solve this.
Can you give me a hint, please?
PL/SQL record types are not object-oriented constructs. So we can't use them as flexibly as we can actually Objects.
If you want to pass an inline array you need to define your types using SQL:
create or replace type t_order_item is object
( product_id number
, quantity number);
/
create or replace type to_order_list is table of t_order_item;
/
Note that this means you can no longer use %TYPE referencing to define the attributes of t_order_item.
Now your call to the procedure will look like this:
begin
pkg_order_management.prc_create_order(240
, to_order_list(t_order_item(2289, 2)
, t_order_item(2058, 5)
), v_order_id);
end;
/
The proper call would be this:
pkg_order_management.prc_create_order(
240,
pkg_order_management.to_order_list(
t_order_item(2289, 2),
t_order_item(2058, 5)
),
v_order_id
);
... but do you really want such an ugly code?
pkg_order_management.prc_create_order(240, v_order_item, v_order_id); looks more beautiful to me.
You need to use OBJECT not RECORD.
Please red this:
https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/objects.htm
Then you can use this nice notation:
CREATE TYPE t_order_item AS OBJECT (
product_id NUMBER,
quantity NUMBER
);
/
CREATE TYPE to_order_list IS TABLE OF t_order_item;
/
DECLARE
v_order_id NUMBER;
PROCEDURE prc_create_order (a NUMBER, order_list to_order_list, o_order_id OUT NUMBER)
IS
BEGIN
NULL;
END;
BEGIN
prc_create_order(240, to_order_list(
t_order_item(2289, 2),
t_order_item(2058, 5)
)
, v_order_id);
END;
/
Our product has several components that can be installed separately and supports oracle. But we do not grant Create type privilege. So during installation of one of the components, I need to ask customer to install the component, add Create type privilege and then run my component.
In the SQL file that will create functions I was planning to give following code:
CREATE OR REPLACE FUNCTION get_some_data (input INT)
RETURN my_table
AS
my_table_var my_table := my_table ();
ret_code INT := 0;
BEGIN
ret_code := create_my_type_and_table ();
IF 1 = ret_code
THEN
NULL; -- add some data to my_table_var here
END IF;
RETURN my_table_var;
END get_some_data;
/
The function create_my_type_and_table would use execute immediate to create record and table type.
Obviously, the problem is since function get_some_data says it will return my_table, compilation fails.
I wanted to know:
is there a way out?
the reason why I want to create and return table is because I need to return multiple fields. All of them are int. Is there a way I can return multi dimensional array, perhaps system collection? I tried sys.odcinumberlist but I did not find a way by which I can return 4 columned sys.odcinumberlist.
If you want some anonymous generic stuff, you could use TABLE OF [TYPE].
Here an example how to build a table of number which is used by another type "table of table of number".
Like every type, you can create them within your schema to use it between different plsql-blocks or return-value in functions.
declare
TYPE tableOfNumber is Table of Number; -- Define a row of numbers
TYPE tableOfTableOfNumer is Table of tableOfNumber; -- define a table of rows
tableMaster tableOfTableOfNumer := tableOfTableOfNumer(); -- init tables
tableChild1 tableOfNumber := tableOfNumber();
tableChild2 tableOfNumber := tableOfNumber();
begin
tableChild1.Extend; -- add a new number-field to our table
tableChild1(1) := 0; -- set the value to the new field
tableChild1.Extend;
tableChild1(2) := 1;
tableChild2.Extend;
tableChild2(1) := 2;
tableChild2.Extend;
tableChild2(2) := 3;
tableMaster.Extend; -- add a new 'row' to out table
tableMaster(1) := tableChild1; -- fill the new 'row' with the fields
tableMaster.Extend;
tableMaster(2) := tableChild2;
-- loop through our 'table'
for r in 1 .. tableMaster.Count
LOOP
for c in 1 .. tableMaster(r).Count
LOOP
dbms_output.put_line(tableMaster(r)(c));
END LOOP;
END LOOP;
end;
If you want to create a function you have to:
1. Declare types in you schema
(an grant rights to the users)
CREATE TYPE tableOfNumber AS TABLE OF NUMBER;
CREATE TYPE tableOftableOfNumber AS TABLE OF tableOfNumber;
2. Create you function:
(here with same code like the plsql-block)
CREATE OR REPLACE FUNCTION DINTER.MyFunction
RETURN tableOftableOfNumber
Is
tableMaster tableOftableOfNumber := tableOftableOfNumber(); -- init tables
tableChild1 tableOfNumber := tableOfNumber();
tableChild2 tableOfNumber := tableOfNumber();
begin
tableChild1.Extend; -- add a new number-field to our table
tableChild1(1) := 0; -- set the value to the new field
tableChild1.Extend;
tableChild1(2) := 1;
tableChild2.Extend;
tableChild2(1) := 2;
tableChild2.Extend;
tableChild2(2) := 3;
tableMaster.Extend; -- add a new 'row' to out table
tableMaster(1) := tableChild1; -- fill the new 'row' with the fields
tableMaster.Extend;
tableMaster(2) := tableChild2;
RETURN tableMaster;
end MyFunction;
/
3. Call the function:
declare
tableMaster tableOfTableOfNumber;
begin
tableMaster := myfunction();
-- loop through our 'table'
for r in 1 .. tableMaster.Count
LOOP
for c in 1 .. tableMaster(r).Count
LOOP
dbms_output.put_line(tableMaster(r)(c));
END LOOP;
END LOOP;
end;
I have to create a list of RECORD and I need to send it to a procedure.
There is my header.
CREATE OR REPLACE PACKAGE tema4 IS
TYPE obj IS RECORD(id INTEGER := 0,percent INTEGER := 0);
TYPE listObj IS TABLE OF obj INDEX BY PLS_INTEGER;
PROCEDURE ex1 (p_listObj IN listObj);
END tema4;
My body.
create or replace PACKAGE BODY tema4 IS
PROCEDURE ex1 (p_listObj IN listObj) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Cant reach');
END ex1;
END tema4;
And my code that calls procedure ex1.
DECLARE
TYPE obj IS RECORD(id INTEGER := 0,percent INTEGER := 0);
TYPE listObj IS TABLE OF obj INDEX BY PLS_INTEGER;
v_obj obj;
v_listObj listObj;
BEGIN
FOR v_i IN (SELECT ID,BURSA FROM STUDENTI ORDER BY ID) LOOP
v_obj.id := v_i.id;
v_obj.percent := 50;
v_listObj(v_i.id) := v_obj;
END LOOP;
FOR v_i IN v_listObj.FIRST..v_listObj.LAST LOOP
DBMS_OUTPUT.PUT_LINE(v_listObj(v_i).id || ' - ' ||
v_listObj(v_i).percent);
END LOOP;
tema4.ex1(v_listObj); --this line is with problems
END;
PLS-00306: wrong number or types of arguments in call to 'EX1'
Can someone explain me what is wrong in my code? I also tried to create my type as global, but it won't let me because of 'RECORD' keyword.
Don't declare the types again (new types), use the types already declared in the package spec:
DECLARE
v_obj tema4.obj;
v_listObj tema4.listObj;
BEGIN
FOR v_i IN (SELECT ID,BURSA FROM STUDENTI ORDER BY ID) LOOP
v_obj.id := v_i.id;
v_obj.percent := 50;
v_listObj(v_i.id) := v_obj;
END LOOP;
FOR v_i IN v_listObj.FIRST..v_listObj.LAST LOOP
DBMS_OUTPUT.PUT_LINE(v_listObj(v_i).id || ' - ' ||
v_listObj(v_i).percent);
END LOOP;
tema4.ex1(v_listObj); --this line is with problems
END;
packageA Body
MEMBER FUNCTION getValue (indx IN PLS_INTEGER) RETURN VARCHAR2
IS
colData packageB.vldtnR
BEGIN
colData := packageB.getColumnData(indx);
--I want to output the id and name from the specified index
END;
packageB Header
TYPE vldtnR IS RECORD(
id PLS_INTEGER;
name VARCHAR2(50)
)
packageB Body
TYPE vldtnArryT IS TABLE OF vldtnR INDEX BY PLS_INTEGER;
vldtnArry vldnArryT;
FUNCTION getColumnData(indx IN PLS_INTEGER) IS
BEGIN
IF vldtnArry.EXISTS(indx) = TRUE THEN
RETURN vldtnArry(indx);
END IF;
END;
Code Overview:
vldtnArry pertains to vldArryT (PackageB body)
vldtnArryT pertains to vldtnR (PackageB body)
vldtnR is in PackageB header
Question:
How do I output the id and name of an index in packageA?
In colData you have a record, you can call the fields from the record directly as colData.id and colData.name.
As Maksim said, you have a colData variable which is a record type, so you can refer to the fields as colData.id and colData.name. You want to return both as a single string based on the varchar2 return type and the embedded comment, so you can do:
CREATE PACKAGE BODY packageA AS
FUNCTION getValue (indx IN PLS_INTEGER) RETURN VARCHAR2
IS
colData packageB.vldtnR;
BEGIN
colData := packageB.getColumnData(indx);
return 'Index ' || indx || ' ID ' || colData.id
|| ' name "' || colData.name || '"';
END;
END;
/
Which will return something like this, which you can obviously modify to your desired output:
Index 2 ID 13 name "Thirteen"
The code you posted has numerous other problems, hopefully from retyping it here. SQL Fiddle demo which compiles and lets you see the result for a couple of index values, bases on a manually-populated collection.
I want to make list which will display values, like text item but in list.
My code:
DECLARE
rg_dept RecordGroup;
rg_dname VARCHAR2(4) := 'Name';
dlist_ID Item := Find_Item('PROJESCT.LIST_ID');
nDummy NUMBER;
BEGIN
rg_dept := Find_Group(rg_dname);
-- Delete any existing Group first
IF NOT Id_Null(rg_dept) THEN
Delete_Group(rg_dept);
END IF;
-- Now create a Record Group using a SQL query
-- Your Query must have a Label and a Value (two Columns)
-- and the data types must match your item type
rg_dept := Create_Group_From_Query(rg_dname,'SELECT department_name, to_char(department_id) FROM departments');
--Clear the existing List
Clear_List(dlist_ID);
-- Populate the Record Group
nDummy := Populate_Group(rg_dept);
-- Populate the List Item
Populate_List(dlist_ID ,rg_dept);
END;
If I make list item and add this code, form will display nothing; if I remove list, all is ok.
P.S. Trigger: when-list-item-change
I should recommend you to populate your lists from e.g. When-new-Form-Instance. As usual you will get a good idea what you need to do from Forms help (menu 'Help/Online Help'). This is a procedure I have made to simple populate list only by specifying the name of the list item and the select statement to populate the lsit item with.
PROCEDURE populate_list_item (
p_item_name VARCHAR2
, p_select VARCHAR2
) IS
l_rg_id RECORDGROUP;
l_list_id ITEM;
l_err_num PLS_INTEGER;
FUNCTION create_temp_group (
p_select VARCHAR2
) RETURN RECORDGROUP IS
l_rg_id RECORDGROUP;
l_group_name VARCHAR2(30) := 'TMP$RG';
BEGIN
l_rg_id := FIND_GROUP(l_group_name);
--Make sure that record group don't alreay exist
IF NOT ID_NULL(l_rg_id) THEN
DELETE_GROUP(l_rg_id);
END IF;
--Populate the temporary record group
l_rg_id := CREATE_GROUP_FROM_QUERY(l_group_name, p_select);
RETURN l_rg_id;
END create_temp_group;
BEGIN
l_rg_id := create_temp_group(p_select);
l_err_num := Populate_Group(l_rg_id);
--Allow for no data found in the selection query
IF l_err_num NOT IN (0, 1403) THEN
RAISE Form_Trigger_Failure;
END IF;
l_list_id := Find_Item(p_item_name);
IF ID_NULL(l_list_id) THEN
RAISE Form_Trigger_Failure;
END IF;
Populate_List(l_list_id, l_rg_id);
Delete_Group(l_rg_id);
END populate_list_item;
The When-New-Form-Instance is a form level trigger and should be under the form (instead of block or item):
The best thing to do is to build all your record groups at design time. It does not slow down the performance unless you have some huge, already slow form. Then populate your list items at run time. Always Copy/paste the code from Forms help.
--Oracle Forms Example: Create a record group from a query, and populate it.
DECLARE
rg_name VARCHAR2(40) := 'Salary_Range';
rg_id RecordGroup;
errcode NUMBER;
BEGIN
/*
** Make sure group doesn't already exist
*/
rg_id := Find_Group( rg_name );
/*
** If it does not exist, create it and add the two
** necessary columns to it.
*/
IF Id_Null(rg_id) THEN
rg_id := Create_Group_From_Query( rg_name,
'SELECT SAL-MOD(SAL,1000) BASE_SAL_RANGE,'
||'COUNT(EMPNO) EMPS_IN_RANGE '
||'FROM EMP '
||'GROUP BY SAL-MOD(SAL,1000) '
||'ORDER BY 1');
END IF;
/*
** Populate the record group
*/
errcode := Populate_Group( rg_id );
END;