Sorting a collection - sorting

I would like to sort a collection type and populate a new collection according to the DESC order of date. How do I go about it?
TYPE xyz IS RECORD(
Item aa.Item%Type,
t_date date,
Code aa.Code%Type,
Qty aa.Units%Type,
Cost aa.Total_Cost%TYPE );
TYPE uxyz IS TABLE OF xyz;
l_uxyz uxyz;

Just to illustrate a very small example, i have mentioned a below
snippet. Hope this will help you out.
-- SQL Table type creation
CREATE OR REPLACE TYPE NUMBER_NTT
IS
TABLE OF NUMBER;
-- Anonomous block to illustrate your question
SET serveroutput ON;
DECLARE
TYPE lv_num_tab
IS
TABLE OF NUMBER;
lv_num1 lv_num_tab:=lv_num_tab();
lv_num2 NUMBER_NTT;
BEGIN
lv_num2:=NUMBER_NTT(1,3,4,7,2,6);
SELECT COLUMN_VALUE BULK COLLECT
INTO lv_num1
FROM TABLE(lv_num2)
ORDER BY 1 DESC;
dbms_output.put_line('With desc order');
FOR i IN lv_num1.FIRST..lv_num1.LAST
LOOP
dbms_output.put_line(lv_num1(i));
END LOOP;
SELECT COLUMN_VALUE BULK COLLECT INTO lv_num1 FROM TABLE(lv_num2);
dbms_output.put_line('Without desc order');
FOR i IN lv_num1.FIRST..lv_num1.LAST
LOOP
dbms_output.put_line(lv_num1(i));
END LOOP;
END;
-------------------------------------OUTPUT-----------------------------------------
anonymous block completed
With desc order
7
6
4
3
2
1
Without desc order
1
3
4
7
2
6
------------------------------------------------------------------------------------

Related

PL/SQL Record populate

I have a record as following and I want to populate this with a for loop.
declare
type ch_type is table of record(id number, name varchar2(50));
type ch_type_tab is table of ch_type;
rec_typr ch_type_tab;
begin
for i in (select * from emp) loop
rec_typr.id := i.emp_id;
rec_typr.name := i.first_name;
end loop;
for i in rec_typr.first..rec_typr.last
loop
dbms_output.put_line(rec_typr(i).id);
end loop;
end;
but I get the error:
PLS:0302 component first must be declared.
Can you help me with this?
Two things are problematic in your code.
1) type ch_type is table of record is syntactically incorrect. You must first declare a record and then define its collection type.
2) Using implicit cursor loop is not an efficient method to load a collection and definitely can't be done the way you're trying to do. Use much simpler BULK COLLECT method instead.
declare
type ch_type is record(id number, name varchar2(50));
type ch_type_tab is table of ch_type;
rec_typr ch_type_tab;
begin
select emp_id,first_name bulk collect into
rec_typr from emp;
for i in rec_typr.first..rec_typr.last
loop
dbms_output.put_line(rec_typr(i).id);
end loop;
end;
/
Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PL/SQL procedure successfully completed.
EDIT
I need to populate the record through a loop not through bulk collect.
Is it any way?
Yes, there is. But, it is less efficient than the method described above.
declare
type ch_type is record(id number, name varchar2(50));
type ch_type_tab is table of ch_type;
rec_typr ch_type_tab := ch_type_tab();
i INTEGER := 1;
begin
rec_typr.extend;
for rec in
(
select emp_id,first_name bulk collect into
rec_typr from emp
)
loop
rec_typr(i).id := rec.emp_id;
rec_typr(i).name := rec.first_name;
rec_typr.extend;
i := i + 1;
end loop;
for i in rec_typr.first..rec_typr.last
loop
dbms_output.put_line(rec_typr(i).id);
end loop;
end;
/

how to look up another field in Oracle Function

Table 1
ID
----------
1
2
3
4
5
Table 2
ID Desc
------------------------------
A1 Apple
A2 Pear
A3 Orange
I am trying to create a Function in Oracle, so that it add the prefix 'A' in Table 1, and after that I want to look up in Table 2 to get the DESC returned. It has to be a function.
Thank you!!!
You may use the following for creation of such a function :
Create or Replace Function Get_Fruit( i_id table2.description%type )
Return table2.description%type Is
o_desc table2.description%type;
Begin
for c in ( select description from table2 where id = 'A'||to_char(i_id) )
loop
o_desc := c.description;
end loop;
return o_desc;
End;
where
no need to include exception handling, because of using cursor
instead of select into clause.
using table_name.col_name%type for declaration of data types for
arguments or variables makes the related data type of the columns
dynamic. i.e. those would be able to depend on the data type of the
related columns.
the reserved keywords such as desc can not be used as column names
of tables, unless they're expressed in double quotes ("desc")
To call that function, the following might be preferred :
SQL> set serveroutput on
SQL> declare
2 i_id pls_integer := 1;
3 o_fruit varchar2(55);
4 begin
5 o_fruit := get_fruit( i_id );
6 dbms_output.put_line( o_fruit );
7 end;
8 /
Apple
PL/SQL procedure successfully completed
I am not sure with your question- Are you trying to achieve something like this:-
CREATE OR REPLACE FUNCTION Replace_Value
(
input_ID IN VARCHAR2
) RETURN VARCHAR2
AS
v_ID varchar(2);
BEGIN
begin
SELECT distinct a.ID into v_id from Table 2 a where a.ID in (select 'A'||b.id from table1 b where b.id=input_ID);
exception
when others then
dbms_output.put_line(sqlcode);
end;
RETURN v_id;
END Replace_Value;
Are you trying for something like this?
CREATE OR replace FUNCTION replace_value (table_name IN VARCHAR2,
input_id IN INTEGER)
RETURN VARCHAR2
AS
v_desc VARCHAR(20);
BEGIN
SELECT descr
INTO v_desc
FROM table2
WHERE id = 'A' || input_id
AND ROWNUM = 1; -- only needed if there are multiple rows for each id.
RETURN v_desc;
END replace_value;
You may also add an exception handling for NO_DATA_FOUND or INVALID_NUMBER

Oracle PL/SQL 6504 with BULK COLLECT

I have this simple query:
SELECT MEASURE_ID, MEASURE_VALUE FROM MY_TABLE;
At the moment returning just a couple of records (in the future there will plenty of them):
8 265.7
7 559.6
A DESC on such table provides:
Name Null Type
------------ -------- ------------
MEASURE_ID NOT NULL NUMBER
MEASURE_VALUE NUMBER(10,1)
Then I defined the proper PL/SQL types:
CREATE OR REPLACE TYPE HASHMAP_NUM_TYPE_OBJ AS OBJECT (
THE_ID NUMBER,
THE_VALUE NUMBER(10,1)
);
CREATE OR REPLACE TYPE HASHMAP_NUM_TYPE IS TABLE OF HASHMAP_NUM_TYPE_OBJ;
And tried to fetch the records using a BULK COLLECT:
stats_by_measure HASHMAP_NUM_TYPE;
...
OPEN cursor_1 FOR
SELECT MEASURE_ID, MEASURE_VALUE
FROM MY_TABLE;
...
FETCH cursor_1 BULK COLLECT INTO stats_by_measure;
...
CLOSE cursor_1;
But I have the Oracle -6504 error. What am I doing wrong?
Remark: If I fetch the same cursor row by row, using a codeblock like this:
foo NUMBER;
faa NUMBER(10,1);
my_obj HASHMAP_NUM_TYPE_OBJ;
...
LOOP
FETCH cursor_1 INTO foo, faa;
my_obj := HASHMAP_NUM_TYPE_OBJ(foo,faa);
EXIT WHEN cursor_1%NOTFOUND;
END LOOP;
everything works fine!
modify your cursor query like below so that it will have the same type
OPEN cursor_1 FOR
SELECT HASHMAP_NUM_TYPE_OBJ(MEASURE_ID, MEASURE_VALUE)
FROM MY_TABLE;
You can only BULK COLLECT objects into a table of objects. In your case:
SQL> CREATE OR REPLACE TYPE hashmap_num_type_obj AS OBJECT (
2 the_id NUMBER,
3 the_value NUMBER(10,1)
4 );
5 /
Type created
SQL> CREATE OR REPLACE TYPE hashmap_num_type IS TABLE OF hashmap_num_type_obj;
2 /
Type created
SQL> DECLARE
2 l_tab hashmap_num_type;
3 BEGIN
4 SELECT hashmap_num_type_obj(measure_id, measure_value)
5 BULK COLLECT INTO l_tab
6 FROM my_table;
7 END;
8 /
PL/SQL procedure successfully completed
I've solve your question
declare
type REC_TYPE is record (
THE_ID number,
THE_VALUE number
);
type TB_TYPE is table of REC_TYPE index by binary_integer;
TBL TB_TYPE;
cursor CURSOR_1 is
select a1.MEASURE_ID A$1, a1.MEASURE_VALUE A$2
from MY_TABLE a1;
type REF_CUR_ is ref cursor return CURSOR_1%rowtype;
CURSOR_2 REF_CUR_;
begin
open CURSOR_2 for
select a1.MEASURE_ID A$1, a1.MEASURE_VALUE A$2
from MY_TABLE a1;
fetch CURSOR_2 bulk collect into TBL ;
close CURSOR_2;
return;
end;
It's works.
I have found another way without ref cursor there (look for FETCH Statement with BULK COLLECT Clause)
You should retrieve the rows into a type based on a record type rather than an object type. The following works;
DECLARE
TYPE hashmap_num_type_rt IS RECORD
(THE_ID NUMBER,
THE_VALUE NUMBER(10,1)
);
TYPE hashmap_num_type_t IS TABLE OF hashmap_num_type_rt;
stats_by_measure hashmap_num_type_t;
BEGIN
SELECT measure_id, measure_value
BULK COLLECT INTO stats_by_measure
FROM my_table;
FOR i IN 1..stats_by_measure.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('RECORD '||TO_CHAR(i)||' : ID - '||stats_by_measure(i).the_id||' MeasureVal: '||TO_CHAR(stats_by_measure(i).the_value));
END LOOP;
END;
You could also define a cursor and the create a table type based on the type of the cursor (which is of course is still a row type rather than an object type).
If you want to use a cursor as the row type then try the following;
DECLARE
CURSOR c_measures IS
SELECT measure_id, measure_value
FROM my_table;
TYPE hashmap_num_type_t IS TABLE OF c_measures%ROWTYPE;
stats_by_measure hashmap_num_type_t;
BEGIN
OPEN c_measures;
FETCH c_measures
BULK COLLECT INTO stats_by_measure;
CLOSE c_measures;
FOR i IN 1..stats_by_measure.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('RECORD '||TO_CHAR(i)||' : ID - '||stats_by_measure(i).measure_id||' MeasureVal: '||TO_CHAR(stats_by_measure(i).measure_value));
END LOOP;
END;

Oracle PL/SQL - Show results of declared table

I am using Toad. I have a declaration of a table in a package as follows:
TYPE MyRecordType IS RECORD
(ID MyTable.ID%TYPE
,FIELD1 MyTable.FIELD1%TYPE
,FIELD2 MyTable.FIELD2%TYPE
,FIELD3 MyTable.FIELD3%TYPE
,ANOTHERFIELD VARCHAR2(80)
);
TYPE MyTableType IS TABLE OF MyRecordType INDEX BY BINARY_INTEGER;
There is a procedure (lets say MyProcedure), that is using an object of this table type as input/output. I want to run the procedure and see the results (how the table is filled). So I am thinking I will select the results from the table:
declare
IO_table MyPackage.MyTableType;
begin
MyPackage.MyProcedure (IO_table
,parameter1
,parameter2
,parameter3);
select * from IO_table;
end;
I get the message:
Table or view does not exist (for IO_table). If I remove the select line, the procedure runs successfully, but I cannot see its results. How can I see the contents of IO_table after I call the procedure?
You cannot see the results for a PL/SQL table by using Select * from IO_table
You will need to loop through the collection in the annonymous block.
do something like, given in pseudo code below...
declare
IO_table MyPackage.MyTableType;
l_index BINARY_INTEGER;
begin
MyPackage.MyProcedure (IO_table
,parameter1
,parameter2
,parameter3);
l_index := IO_table.first;
While l_index is not null
loop
dbms_output.put_line (IO_table(l_index).id);
.
.
.
.
l_index :=IO_table.next(l_index_id);
end loop;
end;
You have to do it like this:
select * from TABLE(IO_table);
and, of course you missed the INTO or BULK COLLECT INTO clause
1) You can not use associated arrays in SELECT statement, Just nested tables or varrays declared globally.
2) You should use TABLE() expression in SELECT statement
3) You can't simply use SELECT in PL/SQL code - cursor FOR LOOP or REF CURSOR or BULK COLLECT INTO or INTO must be used.
4) The last but not least - please study the manual:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjcol.htm#ADOBJ00204
Just an example:
SQL> create type t_obj as object( id int, name varchar2(10));
2 /
SQL> create type t_obj_tab as table of t_obj;
2 /
SQL> var rc refcursor
SQL> declare
2 t_var t_obj_tab := t_obj_tab();
3 begin
4 t_var.extend(2);
5 t_var(1) := t_obj(1,'A');
6 t_var(2) := t_obj(2,'B');
7 open :rc for select * from table(t_var);
8 end;
9 /
SQL> print rc
ID NAME
---------- ----------
1 A
2 B

Find out if a collection was populated by bulk collect

I created an oracle Object Type like this:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_ROWTYPE" AS OBJECT
(
REC_VALUE_DATE DATE
)
/
And then a collection based on this type:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_TABLETYPE" IS TABLE OF RPT_WIRE_IMPORT_RowType;
/
Now I populate the collection using oracle bulk collect into syntax inside a procedure.
So now i want to test if the collection actually got populated, and i am not sure how to do it.
I tried looking it up:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjcol.htm#autoId17 but I am not able to find what I need.
I also have another question. When the procedure bulk collects data into collections, does the data in the collection become permanent as in a table? Or is it semi-permanent...i.e. only lives for the session...as in a temp table.
I suspect you are looking for the COUNT method, i.e.
DECLARE
l_local_collection dbfowner.rpt_wire_import_tabletype;
BEGIN
SELECT sysdate + level
BULK COLLECT INTO l_local_collection
FROM dual
CONNECT BY level <= 10;
dbms_output.put_line( 'l_local_collection contains ' ||
l_local_collection.count ||
' elements.' );
END;
Like any local variable, l_local_collection will have the scope of the block in which it is declared. The data is stored in the PGA for the session. The data in a collection is not permanent.
You can select from the local collection
SQL> create type some_object as object (
2 rec_value_date date
3 );
4 /
Type created.
SQL> create type some_coll
2 as table of some_object;
3 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for x in (select * from table( l_local_collection ))
9 loop
10 dbms_output.put_line( x.rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
but it generally doesn't make sense to go through the effort of pulling all the data from the SQL VM into the PL/SQL VM only to then pass all of the data back to the SQL VM in order to issue the SELECT statement. It would generally make more sense to just keep the data in SQL or to define a pipelined table function to return the data.
If you merely want to iterate over the elements in the collection
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for i in 1 .. l_local_collection.count
9 loop
10 dbms_output.put_line( l_local_collection(i).rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
It would make much more sense to iterate over the elements in the collection, which keeps everything in PL/SQL, than to SELECT from the collection, which forces all the data back into the SQL VM.

Resources