ORACLE use custom tableObject into a function - oracle

I want insert a cursor into my custom tableObject, but it is not always found.
My RECORD:
create or replace type "RECORDRANKING" as object
(
-- Attributes
COL1 NUMBER,
COL2 VARCHAR(50),
COL3 NUMBER
-- Member functions and procedures
-- member procedure <ProcedureName>(<Parameter> <Datatype>)
)
This is object table:
CREATE OR REPLACE TYPE "TBRANKING" AS TABLE OF RECORDRANKING;
Now I go into creating a function (into a package):
CREATE OR REPLACE PACKAGE BODY PKLBOTTONI as
FUNCTION testlb
(
p_gapup IN NUMBER,
p_gadown IN NUMBER
)
RETURN SYS_REFCURSOR IS
cursor_ranking SYS_REFCURSOR;
position NUMBER ;
gap_ranking TBRANKING;
gaprecord RECORDRANKING;
upgap NUMBER;
downgap NUMBER;
BEGIN
select * into cursor_ranking from(
select pkranking.getRanking( 100 ) from dual);
LOOP
FETCH cursor_ranking INTO gap_ranking;
EXIT WHEN cursor_ranking%NOTFOUND;
INSERT INTO gap_ranking (COL1,COL2,COL3)
VALUES
(cursor_ranking.C1,
cursor_ranking.C2,
cursor_ranking.C3);
END LOOP;
return gap_ranking;
END;
END PKLBOTTONI;
I always get:
Compilation errors for PACKAGE BODY PKLBOTTONI
Error: PL/SQL: ORA-00942: table or view does not exist
Line: 32
Text: INSERT INTO gap_ranking

In the loop you're both fetching into "gap_ranking" and then trying to "insert into" it again. Insert into a collection is not a valid syntax. Fetch is ok, either in a loop or using bulk collect to fetch multiple records at once.
From your excerpt it doesn't look like you have a physical database table, so the way to do it in PL/SQL would be something like below.
For more help using collections you can check Oracle docs below too:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm
SQL> set serveroutput on
SQL> declare
2 gap_ranking tbranking := tbranking(null); -- initialize
3 begin
4 gap_ranking.delete; -- clear empty record
5 for cur in
6 (select level as i from dual connect by level <= 5)
7 loop
8 -- insert empty
9 gap_ranking.extend;
10 -- attribute values
11 gap_ranking(gap_ranking.last) := recordranking(1000 + cur.i, 'TEST' || cur.i, 10 + cur.i);
12 end loop;
13 -- loop to print - just to illustrate
14 for j in gap_ranking.first .. gap_ranking.last
15 loop
16 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
17 gap_ranking(j).col2 || ',' ||
18 gap_ranking(j).col3);
19
20 end loop;
21 -- same as...
22 for j in 1 .. gap_ranking.count
23 loop
24 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
25 gap_ranking(j).col2 || ',' ||
26 gap_ranking(j).col3);
27
28 end loop;
29 end;
30 /
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
PL/SQL procedure successfully completed
SQL>

Related

PL/SQL error reference to uninitialised collection error even when its initialised

I have a PL/SQL script which used nested table. Below is the sample code.
type rec is record
(
--col data types here
)
type rec_table is table of rec;
v_rec_table rec_table := rec_table(); --initialising here.
arr_size integer := 0; --edit 1
...
...
begin
...
...
open cursor;
loop
fetch cursor bulk collect into v_rec_table limit arr_size; --edit
if nvl(v_rec_table.last,0) > 0 --it shows error is here.
then
...
...
end if;
The error is ORA-06531: Reference to uninitialized collection. Please help me debug this one.
If would help if you posted complete (sample) code (instead of "...").
Here's an example based on Scott's schema which works OK (your error line is line 14):
SQL> declare
2 type rec is record (id number);
3 type rec_table is table of rec;
4 v_rec_table rec_table := rec_table();
5
6 cursor c1 is select empno from emp;
7 begin
8 open c1;
9 loop
10 exit when c1%notfound;
11 fetch c1 bulk collect into v_rec_table;
12 end loop;
13 dbms_output.put_line('last = ' || v_rec_table.last);
14 if nvl(v_rec_table.last, 0) > 0 then
15 dbms_output.put_line('last exists');
16 end if;
17 close c1;
18 end;
19 /
last = 12
last exists
PL/SQL procedure successfully completed.
Though, I'm not sure what's that LOOP used for, as you could have done it as
SQL> declare
2 type rec is record (id number);
3 type rec_table is table of rec;
4 v_rec_table rec_table := rec_table();
5 begin
6 select empno bulk collect into v_rec_table from emp;
7 dbms_output.put_line('last = ' || v_rec_table.last);
8 end;
9 /
last = 12
PL/SQL procedure successfully completed.
Although your problem is not reproducible, there are few things I found could be improved in your code.
If you are using a bulk collect, the collection is initialised automatically and hence, this line is not required
v_rec_table rec_table := rec_table();
Also, as #Littlefoot mentioned, LOOP is not required unless you are using the LIMIT clause.
You may use the COUNT collection function instead of nvl(v_rec_table.last,0)
v_rec_table.count
Instead of defining a record type and bulk collecting into it, you may define a collection of cursor%ROWTYPE;
CURSOR cur is SELECT column1,column2 FROM tablename;
type rec_table is table of cur%ROWTYPE;
v_rec_table rec_table;
Apologies since i did not post whole code. The error occurred because i did not add index by to two columns in the record definition.
After reading through few articles i found my flaw.

Plsql oracle apex

i am using this loop in my procedure and taking the email ids in i but when i am trying to run i am getting no data found error, so i want to check the values storing in i
code used:
for i in ( select EMAIL
into l_user_mail
from employee A CONNECT BY PRIOR lower(EMAIL) = lower(MANAGER_EMAIL)
START WITH lower(GUID) in (select replace(lower(group_name),'_org_slack')
from dynamic_group where id = '81')
) loop
l_user_mail:=l_user_mail || i.EMAIL;
end loop;
how to check the return value of i in sql command prompt.
i want to see the values getting in i
i want to see the values getting in i
Your FOR LOOP syntax is not correct. INTO clause should not be used in For Loop. See below how you can do that.
FOR I IN
(SELECT EMAIL
FROM EMPLOYEE A
CONNECT BY PRIOR LOWER (EMAIL) = LOWER (MANAGER_EMAIL)
START WITH LOWER (GUID) IN (
SELECT REPLACE (LOWER (GROUP_NAME), '_org_slack')
FROM DYNAMIC_GROUP
WHERE ID = '81') )
LOOP
-- l_user_mail:=:P60_IDP_GROUPS;
L_USER_MAIL := L_USER_MAIL || I.EMAIL;
-- To display value of I
DBMS_OUTPUT.PUT_LINE (I.EMAIL);
END LOOP;
Demo:
SQL> DECLARE
2 L_USER_MAIL VARCHAR2 (10);
3 BEGIN
4 FOR I IN (SELECT LEVEL
5 FROM DUAL
6 CONNECT BY LEVEL < 10)
7 LOOP
8 -- l_user_mail:=:P60_IDP_GROUPS;
9 L_USER_MAIL := L_USER_MAIL || I.level;
10 DBMS_OUTPUT.PUT_LINE (I.level);
11 END LOOP;
12 END;
13 /
1
2
3
4
5
6
7
8
9
PL/SQL procedure successfully completed.

Oracle stored function - pass table name as parameter

I'm trying to create a stored function in Oracle that will count the table rows..i want to make the table name dynamic, so i passed it as a parameter, the stored function code looks like this
create type tes_jml_obj is object(jumlah integer);
create type tes_jml_table is table of tes_jml_obj;
create or replace function jumlahBaris(namatabel varchar)
return tes_jml_table
is
tabel tes_jml_table := tes_jml_table();
begin
for r in (execute immediate 'select count(*) as jumlah from' || namatabel)
loop
tabel.extend;
tabel(1) := tes_jml_obj(r.jumlah);
end loop;
return tabel;
end;
But when i execute it, it returns errors. Am i missing something here? Is that the correct way to get the table rows dynamically?
There is a space missing after FROM keyword in the EXECUTE IMMEDIATE statement.
EXECUTE IMMEDIATE statement has a syntax error. You are missing the INTO clause.
You cannot use EXECUTE IMMEDIATE inside a CURSOR FOR LOOP. Basically, you are returning nothing from the execute immediate statement as mentioned in point 2 above.
The iteration syntax for the LOOP is not correct. The syntax is FOR r IN 1..COUNT().
After rectifying your code, this is how it would look like:
SQL> CREATE OR REPLACE TYPE TES_JML_OBJ IS OBJECT(JUMLAH NUMBER)
2 /
Type created.
SQL> CREATE OR REPLACE TYPE TES_JML_TABLE IS TABLE OF TES_JML_OBJ
2 /
Type created.
SQL> CREATE OR REPLACE
2 FUNCTION jumlahBaris(
3 namatabel VARCHAR2)
4 RETURN tes_jml_table
5 IS
6 TABEL TES_JML_TABLE := TES_JML_TABLE();
7 cnt NUMBER;
8 BEGIN
9 EXECUTE IMMEDIATE 'select count(*) as jumlah from ' || NAMATABEL INTO CNT;
10 FOR R IN 1..CNT
11 LOOP
12 TABEL.EXTEND;
13 TABEL(R) := TES_JML_OBJ(R);
14 dbms_output.put_line(TES_JML_OBJ(R).jumlah);
15 END LOOP;
16 RETURN tabel;
17 END;
18 /
Function created.
SQL> SHO ERR
No errors.
So, the function compiled with no errors. Let's execute it and see the output:
SQL> SET SERVEROUTPUT ON
SQL> SELECT JUMLAHBARIS('EMP') FROM DUAL;
JUMLAHBARIS('EMP')(JUMLAH)
--------------------------------------------------------------------------------
TES_JML_TABLE(TES_JML_OBJ(1), TES_JML_OBJ(2), TES_JML_OBJ(3), TES_JML_OBJ(4), TE
S_JML_OBJ(5), TES_JML_OBJ(6), TES_JML_OBJ(7), TES_JML_OBJ(8), TES_JML_OBJ(9), TE
S_JML_OBJ(10), TES_JML_OBJ(11), TES_JML_OBJ(12), TES_JML_OBJ(13), TES_JML_OBJ(14
))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQL>
Your execute immediate will return only one value, the count, so what is there to loop over?
Also I'm not sure that execute immediate works with an implicit cursor.
In your SQL it looks like you don't have a space after the from keyword.
Try something like this instead:
create or replace function jumlahBaris(namatabel varchar)
return tes_jml_table
is
tabel tes_jml_table := tes_jml_table();
the_count integer;
the_sql varchar(100);
begin
the_sql := 'select count(*) as jumlah from ' || namatabel;
execute immediate the_sql INTO the_count;
if the_count IS NOT NULL THEN
tabel.extend;
tabel(1) := tes_jml_obj(the_count);
end if;
return tabel;
end;

DELETE Collection Method in oracle work wrong

I want to use DELETE Collection Method to delete some elements in collection
such as:
create or replace procedure testloop3 (clearaaa out nestedtable) as
type nestedtable is table of varchar2(255);
reply_ref_messageIds nestedtable;
getDelete_messageIds nestedtable;
begin
select distinct r.messagebox_id bulk collect into reply_ref_messageIds from reply r;
select m.id bulk collect into getDelete_messageIds from messagebox m;
getDelete_messageIds.delete(2);
getDelete_messageIds.delete(4);
getDelete_messageIds.delete(7);
getDelete_messageIds.delete(11);
getDelete_messageIds.delete(13);
for i in getDelete_messageIds.FIRST .. getDelete_messageIds.LAST loop
dbms_output.put_line(i);
end loop;
for i in 5 .. 12 loop
dbms_output.put_line(i);
end loop;
end;
and then I debug this procedure with plsql dev
-- Created on 2013/4/4 by THINKPAD
declare
-- Local variables here
aa nestedtable;
begin
-- Test statements here
testloop3(aa);
end;
and I get the indexes of getDelete_messageIds before remove which are 1 to 15.
However: when I debug to getDelete_messageIds.delete(2); it removes index 1 and 2...I can't explain why.
And then when I debug next statement getDelete_messageIds.delete(4); it removes index 3 and 4. And then getDelete_messageIds.delete(7); only removes index 7...
I can't understand...
your procedure, as posted, is showing nothing of the sort. you are simply looping around like
for idx in 1..15 loop
(.FIRST will resolve to 1 and .LAST will resolve to 15). it does not mean there are still 15 elements in the NT.
you are not checking to see if the indexes are deleted. I think you're confused about the proper way to loop through a nested table where there are gaps.
i.e. you can see the elements are deleted:
SQL> create table messagebox(id ) as select to_char(rownum) from dual connect by level <= 15;
Table created.
SQL> create or replace procedure testloop3
2 as
3 type nestedtable is table of varchar2(255);
4 getDelete_messageIds nestedtable;
5 v_idx number;
6 begin
7 select m.id bulk collect into getDelete_messageIds from messagebox m;
8 getDelete_messageIds.delete(2);
9 getDelete_messageIds.delete(4);
10 getDelete_messageIds.delete(7);
11 getDelete_messageIds.delete(11);
12 getDelete_messageIds.delete(13);
13 v_idx := getDelete_messageIds.first;
14 while v_idx is not null
15 loop
16 dbms_output.put_line(v_idx);
17 v_idx := getDelete_messageIds.next(v_idx);
18 end loop;
19 end;
20 /
Procedure created.
SQL> exec testloop3
1
3
5
6
8
9
10
12
14
15
so 2, 4, 7, 11, 13 are deleted. exactly as expected.
you can see
DECLARE
TYPE NumList IS TABLE OF NUMBER;
n NumList := NumList(1,3,5,7);
counter INTEGER;
BEGIN
DBMS_OUTPUT.PUT_LINE('N''s first subscript is ' || n.FIRST);
DBMS_OUTPUT.PUT_LINE('N''s last subscript is ' || n.LAST);
-- When the subscripts are consecutive starting at 1,
-- it's simple to loop through them.
FOR i IN n.FIRST .. n.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('Element #' || i || ' = ' || n(i));
END LOOP;
n.DELETE(2); -- Delete second element.
-- When the subscripts have gaps
-- or the collection might be uninitialized,
-- the loop logic is more extensive.
-- Start at the first element
-- and look for the next element until there are no more.
IF n IS NOT NULL THEN
counter := n.FIRST;
WHILE counter IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE
('Element #' || counter || ' = ' || n(counter));
counter := n.NEXT(counter);
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE('N is null, nothing to do.');
END IF;
END;
reference Finding the First or Last Collection Element

How can I populate in memory tables, in PLSQL?

I don't know if it is the correct terminology but I call "in memory tables" to the objects created like this:
create type InMemReg is object (field1 varchar2(10), field2 varchar2(20), field3 number);
create type InMemTab is table of InMemReg;
In this case my "in memory table" is "InMemTab". My question is how can I populate this kind of object, when i don't know previously the numbers of elements? I have seen in some places this type of initialization:
declare
v_uno InMemReg := InMemReg('a','b',1999);
v_dos InMemReg := InMemReg('A','Z',2000);
t_tres InMemTab := InMemTab();
begin
t_tres := InMemTab(v_uno, v_dos);
In this situation I have explicitly 2 objects before initialize "t_tres", but in a dynamic scenario where I could have n numbers of elements I don't know how to populate it.
In another OO language could be something like this:
t_tres.add(OtherObject)
The type InMemTab is a nested table in Oracle parlance.
The equivalent to the add method would be to call the extend method and then to assign OtherObject to the last position in the nested table.
SQL> ed
Wrote file afiedt.buf
1 declare
2 v_uno InMemReg := InMemReg('a','b',1999);
3 v_dos InMemReg := InMemReg('A','Z',2000);
4 t_tres InMemTab := InMemTab();
5 begin
6 t_tres.extend;
7 t_tres( t_tres.count ) := v_uno;
8 t_tres.extend;
9 t_tres( t_tres.count ) := v_dos;
10 dbms_output.put_line( 't_tres has ' || t_tres.count || ' elements.' );
11* end;
12 /
t_tres has 2 elements.
PL/SQL procedure successfully completed.
You can factor that out into an add procedure as well
SQL> ed
Wrote file afiedt.buf
1 declare
2 v_uno InMemReg := InMemReg('a','b',1999);
3 v_dos InMemReg := InMemReg('A','Z',2000);
4 t_tres InMemTab := InMemTab();
5 procedure add( p_nt IN OUT InMemTab,
6 p_elem IN InMemReg )
7 as
8 begin
9 p_nt.extend;
10 p_nt( p_nt.count ) := p_elem;
11 end;
12 begin
13 add( t_tres, v_uno );
14 add( t_tres, v_dos );
15 dbms_output.put_line( 't_tres has ' || t_tres.count || ' elements.' );
16* end;
17 /
t_tres has 2 elements.
PL/SQL procedure successfully completed.
It is common to populate the collection from the data itself, meaning you are not explicitly adding sets of strings and numbers, you're pulling the data in from other tables. Because this is a common and natural thing to do with collections, Oracle made it easy via "BULK COLLECT INTO" clause in pl/sql. For example:
DECLARE
TYPE EmployeeSet IS TABLE OF employees%ROWTYPE;
underpaid EmployeeSet;
-- Holds set of rows from EMPLOYEES table.
CURSOR c1 IS SELECT first_name, last_name FROM employees;
TYPE NameSet IS TABLE OF c1%ROWTYPE;
some_names NameSet;
-- Holds set of partial rows from EMPLOYEES table.
BEGIN
-- With one query,
-- bring all relevant data into collection of records.
SELECT * BULK COLLECT INTO underpaid FROM employees
WHERE salary < 5000 ORDER BY salary DESC;
-- Process data by examining collection or passing it to
-- eparate procedure, instead of writing loop to FETCH each row.
DBMS_OUTPUT.PUT_LINE
(underpaid.COUNT || ' people make less than 5000.');
FOR i IN underpaid.FIRST .. underpaid.LAST
LOOP
DBMS_OUTPUT.PUT_LINE
(underpaid(i).last_name || ' makes ' || underpaid(i).salary);
END LOOP;
-- You can also bring in just some of the table columns.
-- Here you get the first and last names of 10 arbitrary employees.
SELECT first_name, last_name
BULK COLLECT INTO some_names
FROM employees
WHERE ROWNUM < 11;
FOR i IN some_names.FIRST .. some_names.LAST
LOOP
DBMS_OUTPUT.PUT_LINE
('Employee = ' || some_names(i).first_name
|| ' ' || some_names(i).last_name);
END LOOP;
END;
/
You don't typically need to worry about extending or how many elements you'll have, you can usually slurp it in and then use the built in features of the collection as you like (counts, loop through, compare different collections, set operations, etc)

Resources