oracle table of records declared in package can't use index by? - oracle

Thanks for your kindness
I Declare a record type and table of records in spec of package, I need to use this table type as a return of in pipeline function.
If I add INDEX BY (something) to a table declaration, an error compilation in a pipeline function caused. Why can't use INDEX BY?

It's documented that you cannot use index-by (associative arrays) in pipelined functions. you have to use a nested table (either a pl/sql array defined without "index by" or an SQL type).
SQL> create or replace package testpkg
2 as
3 type test_rec is record(id number, id2 number);
4 type test_tab is table of test_rec index by binary_integer;
5
6 function test return test_tab pipelined;
7
8 end;
9 /
Warning: Package created with compilation errors.
SQL> show errors
Errors for PACKAGE TESTPKG:
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/12 PLS-00630: pipelined functions must have a supported collection
return type
SQL> create or replace package testpkg
2 as
3 type test_rec is record(id number, id2 number);
4 type test_tab is table of test_rec;
5
6 function test return test_tab pipelined;
7
8 end;
9 /
Package created.
SQL>

Related

PLS-00355 error whike creating a new table type

I'm getting the PLS-00355 error while trying to create the new type like this:
CREATE OR REPLACE TYPE DAYS_T IS TABLE OF VARCHAR(250) INDEX BY BINARY_INTEGER;
Any clue what is wrong?
Many thanks!
This is what you did and how Oracle responded:
SQL> CREATE OR REPLACE TYPE DAYS_T IS TABLE OF VARCHAR(250) INDEX BY BINARY_INTEGER;
2 /
Warning: Type created with compilation errors.
SQL> show err
Errors for TYPE DAYS_T:
LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/16 PLS-00355: use of pl/sql table not allowed in this context
On the other hand:
SQL> CREATE OR REPLACE TYPE DAYS_T IS TABLE OF VARCHAR2(250);
2 /
Type created.
SQL>
Responding to your comment: if you declared type at PL/SQL level (not SQL), then your code (without create or replace, though) would be OK (line #2 is what you used, literally):
SQL> declare
2 TYPE DAYS_T IS TABLE OF VARCHAR(250) INDEX BY BINARY_INTEGER;
3 begin
4 null;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
You can define a nested table collection type in the SQL scope using:
CREATE OR REPLACE TYPE DAYS_T IS TABLE OF VARCHAR(250);
You can define an associative array collection type in a PL/SQL scope using:
DECLARE
TYPE DAYS_T IS TABLE OF VARCHAR(250) INDEX BY BINARY_INTEGER;
BEGIN
NULL;
END;
/
You could also locally define a nested-table collection type in a PL/SQL scope using:
DECLARE
TYPE DAYS_T IS TABLE OF VARCHAR(250);
BEGIN
NULL;
END;
/
However, you cannot define an associative array collection type in the SQL scope as it is a PL/SQL only data type.
You then asked in comments:
But how can I add an index?
They both have an index.
For example, after declaring the type in SQL, you can use the nested table collection in PL/SQL like this:
DECLARE
v_days DAYS_T;
BEGIN
v_days := DAYS_T(); -- Initialise the collection.
v_days.EXTEND(3); -- Extend the collection by 3 elements.
v_days(1) := 'Monday'; -- Set the first element.
v_days(3) := 'Wednesday'; -- Set the third element.
FOR i IN 1 .. v_days.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( i || ' = ' || v_days(i) );
END LOOP;
END;
/
Which outputs:
1 = Monday
2 =
3 = Wednesday
db<>fiddle here

Oracle, object table & nested tables

Say I have 2 objects MY_OBJ, MY_NESTED_TABLE_OBJ
CREATE OR REPLACE TYPE MY_NESTED_TABLE_OBJ IS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE MY_OBJ AS OBJECT (
simple_atribute NUMBER(6),
table_attribute MY_NESTED_TABLE_OBJ,
MEMBER PROCEDURE doStuff(text VARCHAR2)
) NOT FINAL INSTANTIABLE;
MY_OBJ's table
CREATE TABLE TBL_MY_OBJ OF MY_OBJ
( CONSTRAINT PK_simple_atribute PRIMARY KEY(simple_atribute))
NESTED TABLE table_attribute STORE AS attribute_nst;
How do I insert a VARCHAR2(100) into the nested table belonging to table_attribute?? What is the sintax??
Doing a simple insert like: INSERT INTO attribute_nst VALUES ('some text'); gives the error
cannot reference nested table column's storage table
What i want is to do insert from within PROCEDURE doStuff(text VARCHAR2), i've tried:
INSERT INTO SELF.attribute_nst VALUES (text);
INSERT INTO attribute_nst VALUES (text);
INSERT INTO table_attribute VALUES (text);
...and other combination and no nothing, so please help!
SQL> CREATE OR REPLACE TYPE BODY MY_OBJ AS
2 member procedure doStuff(text varchar2) is
3 begin
4 table_attribute.extend(1);
5 table_attribute(table_attribute.count) := text;
6 end;
7 end;
8 /
Type body created.
SQL> declare
2 l_my_obj My_Obj := My_Obj(1,MY_NESTED_TABLE_OBJ());
3 begin
4 l_my_obj.doStuff('abc');
5 l_my_obj.doStuff('def');
6
7 insert into tbl_my_obj values (l_my_obj);
8 end;
9 /
PL/SQL procedure successfully completed.
SQL> select * from tbl_my_obj;
SIMPLE_ATRIBUTE
---------------
TABLE_ATTRIBUTE
------------------------------------------------------------
1
MY_NESTED_TABLE_OBJ('abc', 'def')
Try this
insert into tbl_my_obj values (1, new my_nested_table_obj(text1, text2));

How can I do forward declarations in Oracle?

Here is my situation: type A wants to have a method that returns a type that is a table of type A entries. Can I do this?
Here is a pair of SQL Type declarations:
SQL> create or replace type a as object
2 ( attr1 number
3 , attr2 date )
4 /
Type created.
SQL> create or replace type a_nt as table of a
2 /
Type created.
SQL>
Now what we want is a method which returns A_NT(). Hmm, let's see:
SQL> alter type a
2 add member function getme (p1 number) return a_nt
3 /
alter type a
*
ERROR at line 1:
ORA-04055: Aborted: "A" formed a non-REF mutually-dependent cycle with "A_NT".
SQL>
Uh-oh. We need another approach. This is the sort of occasion when we should be using inheritance.
SQL> create or replace type abstract_a as object
2 ( attr1 number
3 , attr2 date )
4 not final not instantiable
5 /
Type created.
SQL> create or replace type a_nt as table of abstract_a
2 /
Type created.
SQL> create or replace type a under abstract_a
2 ( member function getme (p1 number) return a_nt )
3 instantiable
4 /
Type created.
SQL>
Looks good. So, we'll add an implementation and then try it out:
SQL> create or replace type body a as
2 member function getme (p1 number) return a_nt
3 is
4 l_nt a_nt;
5 begin
6 select a(empno, hiredate)
7 bulk collect into l_nt
8 from emp
9 where deptno = p1;
10 return l_nt;
11 end;
12 end;
13 /
Type body created.
SQL>
Let's roll!
SQL> set serveroutput on
SQL> declare
2 v a := a(null, null);
3 n a_nt;
4 begin
5 n := v.getme(50);
6
7 for i in n.first()..n.last() loop
8 dbms_output.put_line(n(i).attr1 ||'::'||to_char(n(i).attr2, 'DD-MON-YYYY'));
9 end loop;
10 end;
11 /
8085::08-APR-2010
8060::08-APR-2008
8061::27-FEB-2010
8100::
PL/SQL procedure successfully completed.
SQL>
The bootstrapping is cumbersome but it works. This may be yet another area where Oracle's OOP implementation is incomplete. But perhaps it's just a reflection of the fact that is not a good idea. It took me a while to figure it out, because the underlying model doesn't really make sense. Although maybe with Real World names it would have become obvious :)

Create Oracle procedure error - declare custom type

I'm attempting to create a procedure in Oracle Express Server (Application Express 2.1.0.00.39) using the web interface.
This is the SQL I'm running via the SQL Commands option in the web interface
CREATE OR REPLACE PROCEDURE my_procedure (listOfNumbers num_list,
v_value varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE my_table
SET my_column = v_value
WHERE my_row_id IN (SELECT column_value
FROM TABLE(listOfNumbers));
COMMIT;
END;
UPDATE:
Changed SELECT column_value FROM TABLE to SELECT column_value FROM TABLE(listOfNumbers) but now I get the following error:
PLS-00201: identifier 'num_list' must
be declared
UPDATE 2:
Here is how I created my type:
CREATE OR REPLACE TYPE "num_list" as table of NUMBER(38,1)
/
Seems the error is being caused on the parameter declaration line:
(listOfNumbers num_list, v_value varchar2)
Below is the object details as displayed by the Oracle Database Express Edition web interface.
Try ...TABLE(CAST(listOfNumbers AS num_list)).
The SQL parser simply sees a bind placeholder in place of listOfNumbers, and since it's a custom type you need to tell it what type it is.
This will only work if num_list has been defined as a type in the schema, not just declared as a type in a PL/SQL block.
Your code works - providing the array type has been declared correctly (see below). As you are still having a problem I suspect that is where you are going wrong. But you need to post the code you are using to create the NUM_LIST type in order for us to correct it.
My test data:
SQL> select * from my_table
2 /
MY_COLUMN MY_ROW_ID
-------------------- ----------
APC 1
XYZ 2
JFK 3
SQL>
In order to use a type in a SQL statement we must create it as a SQL object:
SQL> create type num_list as table of number;
2 /
Type created.
SQL>
SQL>
SQL> create or replace procedure my_procedure
2 (listofnumbers num_list,
3 v_value varchar2)
4 is
5 begin
6
7 update my_table
8 set my_column = v_value
9 where my_row_id in (select column_value
10 from table(listofnumbers));
11
12 end;
13 /
Procedure created.
SQL>
Executing the procedure:
SQL> declare
2 n num_list := num_list(1,3);
3 begin
4 my_procedure (n , 'FOX IN SOCKS');
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
And lo!
SQL> select * from my_table
2 /
MY_COLUMN MY_ROW_ID
-------------------- ----------
FOX IN SOCKS 1
XYZ 2
FOX IN SOCKS 3
SQL>
Apparently I was creating the type with quotes around the name:
The following didn't work:
CREATE OR REPLACE TYPE "NUMBER_T" as table of NUMBER(38,1)
When I did it without the quotes and then created the procedure, it was able to recognize it.
The following did work:
CREATE OR REPLACE TYPE NUMBER_T as table of NUMBER(38,1)
I'm not sure why, but it worked.

How to populate an array in an oracle stored procedure?

How to use array( Varray) in store procedure. Actually,i have make a stored procedure from which i retrieve a list of elements.
For example:
create or replace procedure GetTargetFields ( fileformat in varchar2,
filefields out Varray(4) )
IS
BEGIN
SELECT id
INTO filefields
FROM tablename;
END;
use BULK COLLECT INTO:
SQL> CREATE OR REPLACE TYPE vrray_4 AS VARRAY(4) OF VARCHAR2(10);
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE GetTargetFields(fileformat IN VARCHAR2,
2 filefields OUT vrray_4) IS
3 BEGIN
4 SELECT dummy BULK COLLECT INTO filefields FROM dual;
5 END;
6 /
Procedure created
SQL> DECLARE
2 x vrray_4;
3 BEGIN
4 GetTargetFields(NULL, x);
5 END;
6 /
PL/SQL procedure successfully completed
Also make sure that your query doesn't return more than 4 rows (for a VARRAY(4)) or you will run into ORA-22165
Niraj. You should use the principles Vincent provided, but I suggest you use nested table type instead of varray in case you don't need exactly varray type in your logic. This will save you from ORA-22165 error if the query returns more then 4 rows - nested tabled will be automatically expanded to the size needed. You define nested table type as follows:
declare
type TStrTab is table of varchar2(10);
fStrTab TStrTab := TStrTab();
begin
select ... bulk collect into fStrTab from...
end;
More information about PL/SQL collection types can be found in official Oracle PL-SQL User's Guide and Reference Chapter 5.
Two things:
You need to declare a named type -- you can't use VARRAY directly in a parameter declaration. (Unless this has changed in 11g.)
You need to use BULK COLLECT to use a single query to populate a collection.
Example:
CREATE TYPE fieldlist AS VARRAY(4) OF NUMBER;
CREATE PROCEDURE GetTargetFields( filefields OUT fieldlist )
AS
BEGIN
SELECT id BULK COLLECT INTO filefields FROM tablename;
END;

Resources