Creating function that returns table that isn't declared yet - oracle

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;

Related

inserting of data not happening in oracle

I am new to plsql.I have a table where i need to insert the data(some dummy data).So,i thought to use plsql block and using For loop it will insert the data automatically.The plsql block is runned successfully,but the data are stored as empty.The block I tried is:
declare
v_number1 number;
v_number2 number;
v_number3 number;
begin
For Lcntr IN 2..17
LOOP
v_number1 := v_number1+1;
v_number2 := v_number2+2;
v_number3 := v_number3+3;
Insert into stu.result(res_id,stu_id,eng,maths,science) values (stu.seq_no.NEXTVAL,Lcntr,v_number1,v_number2,v_number3);
END LOOP;
end;
But my table is loaded as:(please ignore first two row data,i inserted it manually):
The data for eng,maths,science is not being inserted.why it is happening so?
That's because your variables are NULL. NULL + 1 = NULL as well.
If you modify declaration to
v_number1 number := 0;
v_number2 number := 0;
v_number3 number := 0;
something might happen.

How to populate specific data from database into list item?

I created ListItem1 with List Style (Poplist) for department no (10,20,30) and other ListItem2 with List Style (Tlist) for employees name. Record group for employees name
When i click on department no.10 then populate all employees name data into ListItem2. I want to populate data only employees names who falls under department no.10 or 20 or 30
CODE:
DECLARE
a VARCHAR2(100);
num NUMBER := 10;
BEGIN
a := populate_group ('R1');
populate_list ('LIST1','R1');
END;
"LIST1" for populate employees name data
"R1" is a group record name
Sounds like you just need to add where department_no = :List1 to the R1 record group.
In PL/SQL lists are called collections.
Try something like it:
DECLARE
TYPE population_type IS TABLE OF NUMBER INDEX BY VARCHAR2(64);
country_population population_type;
continent_population population_type;
howmany NUMBER;
which VARCHAR2(64)
BEGIN
country_population('Greenland') := 100000;
country_population('Iceland') := 750000;
howmany := country_population('Greenland');
continent_population('Australia') := 30000000;
continent_population('Antarctica') := 1000; -- Creates new entry
continent_population('Antarctica') := 1001; -- Replaces previous
value
which := continent_population.FIRST; -- Returns 'Antarctica'
-- as that comes first alphabetically.
which := continent_population.LAST; -- Returns 'Australia'
howmany := continent_population(continent_population.LAST);
-- Returns the value corresponding to the last key, in this
-- case the population of Australia.
END;

Calling Oracle procedure with a Type

I want to create a simple table that contains an Oracle UDT, create a procedure that will let me add values, and then test the procedure. I have the following code.
CREATE OR REPLACE TYPE IDS_TYPE AS OBJECT ( id1 NUMBER, id2 NUMBER, id3 NUMBER );
CREATE OR REPLACE TYPE IDS_TABLE AS TABLE OF IDS_TYPE;
CREATE OR REPLACE PROCEDURE getInfo(p_ids IN IDS_TABLE) IS
BEGIN
FOR i IN 1 .. p_ids.COUNT LOOP
dbms_output.put_line(p_ids(i).id1
|| ',' || p_ids(i).id2
|| ',' || p_ids(i).id3);
END LOOP;
END getInfo;
declare
my_data IDS_TABLE;
begin
my_data(1).id1 := 1234;
my_data(1).id2 := 10;
my_data(1).id3 := 10;
my_data(2).id1 := 1234;
my_data(2).id2 := 10;
my_data(2).id3 := 10;
getInfo( my_data );
end;
I get the following error however. Can someone point out the mistake?
Error report -
ORA-06531: Reference to uninitialized collection
ORA-06512: at line 4
06531. 00000 - "Reference to uninitialized collection"
*Cause: An element or member function of a nested table or varray
was referenced (where an initialized collection is needed)
without the collection having been initialized.
*Action: Initialize the collection with an appropriate constructor
or whole-object assignment.
You have to better handle the initialization and the creation of object
variables; for example you could do :
declare
my_data IDS_TABLE;
my_data2 IDS_TABLE;
begin
/* initialize the collection */
my_data := new IDS_TABLE();
/* extend to host a new value */
my_data.extend(1);
/* create the object */
my_data(1) := new IDS_TYPE(1234, 10, 10);
my_data.extend(1);
my_data(2) := new IDS_TYPE(1234, 10, 10);
/* you can change the value of an attribute of a given element of the collection */
my_data(2).id3 := 99999;
getInfo( my_data );
/* OR in a more compact way, as suggested by Peter Lang */
my_data2 := NEW ids_table( NEW ids_type(991234, 9910, 9910), NEW ids_type(991234, 9910, 9910) );
getInfo( my_data2 );
end;

Calling a function in a before delete trigger

id like to call this function:
CREATE OR REPLACE PACKAGE orders_salary_manage2 AS
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER;
END;
CREATE OR REPLACE PACKAGE BODY orders_salary_manage2 AS
tot_orders NUMBER;
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER
IS
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
return v_total_cost;
END;
from this trigger:
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
total_calc(v_old_order);
END;
but i keep getting this error, note there is no error number just this:
Error at line 4: PL/SQL: Statement ignored
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
im new to pl/sql and just not sure what is causing the problem. When a user deletes an order from the orders table the trigger should call the function to add up all the products on the order.
Thank you
(Considering your Package compiled with no errors) Use-
ret_val:= orders_salary_manage2.total_calc(v_old_order);
The ret_val must be a NUMBER since the package function total_calc returns a NUMBER. A function MUST always return its outcoume to a variable (like ret_val) depending on the type of the return value the data type of the variable must be declared.
The syntax to call Pacakaged Procedures and functions is -
<RETURN_VARIABLE> := PACKAGE_NAME.<FUNCTION_NAME>();
PACKAGE_NAME.<PROCEDURE_NAME>(); --Since Procedure never returns
Also note that if your package is in a different SCHEMA and has no PUBLIC SYNONYM then you will have to prefix the schema name like <SCHEMA>.PACKAGE_NAME.<FUNCTION_NAME>() (considering the calling schema has execute permissions on the package).
So,
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
v_ret_val NUMBER := 0;
BEGIN
v_ret_val := orders_salary_manage2.total_calc(v_old_order);
--...Do stuff with v_ret_val
END;

Oracle Forms list does not display values

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;

Resources