record linking to each other in PL/SQL Developer - oracle

There was a need to create records in PL / SQL Developer, which refer to each other. I started to understand all this recently, therefore there were doubts about the achievement of this goal, but also the thought was spinning in my head that I was not the first to ask this question. Therefore, if you know how to do it or have ideas for implementation, I will be very glad of your help, but for now I will continue to google.
Example:
TYPE rtype1 IS RECORD
(
/*some code*/
r_type2 rtype2;
);
TYPE rtype2 IS RECORD
(
/*some code*/
r_type1 rtype1;
);
Additional Information:
The fact is that, on the basis of xsd schemes, you need to generate records and collections, but because In the xsd scheme this is not prohibited, such a need has arisen. And it is necessary to create types, and not to use the tools for working with xml.

You can use forward declaration to declare the existence of a type in PL/SQL before you fully specify it. Like this:
DECLARE
TYPE rtype2; -- Forward declaration
TYPE rtype1 IS RECORD( r_type2 rtype2 );
TYPE rtype2 IS RECORD( r_type1 INTEGER );
BEGIN
NULL;
END;
/
However, you cannot use this to declare a non-REF mutually recursive type; if you try this:
DECLARE
TYPE rtype2;
TYPE rtype1 IS RECORD( r_type2 rtype2 );
TYPE rtype2 IS RECORD( r_type1 rtype1 );
BEGIN
NULL;
END;
/
Then you get the error:
ORA-06550: line 4, column 18:
PLS-00318: type "RTYPE2" is malformed because it is a non-REF mutually recursive type
ORA-06550: line 4, column 3:
PL/SQL: Item ignored
You can do it using object data types and a REF in the SQL scope:
CREATE TYPE otype1; -- Forward declaration
CREATE TYPE otype2 IS OBJECT( o_type1 REF otype1 );
CREATE OR REPLACE TYPE otype1 IS OBJECT( o_type2 otype2 );
But, again, if you try to use a non-REF mutually recursive type:
CREATE TYPE otype1;
CREATE TYPE otype2 IS OBJECT( o_type1 otype1 );
/* ORA-24344: success with compilation error */
CREATE TYPE otype1 IS OBJECT( o_type2 otype2 );
/* ORA-04055: Aborted: "OTYPE1" formed a non-REF mutually-dependent cycle with "OTYPE2". */
Then it doesn't work.
db<>fiddle here

Yes, you can define a type and then reference it in another type, which you define later in the code or on the system. However, as Alex asked, what are you trying to accomplish? -- Also contrary to what mathguy posted you can create and use types whose definitions are co-dependent. I do not recommend doing so, but ...
From the documentation:
An incomplete type is a type created by a forward type definition. It is called incomplete because it has a name but no attributes or methods. It can be referenced by other types, allowing you define types that refer to each other. However, you must fully specify the type before you can use it to create a table or an object column or a column of a nested table type.

Related

Accessing table from an object

Is it possible to access an existing table from an object?
Here the simplified snippet:
CREATE OR REPLACE TYPE MYOBJ AS OBJECT(
MEMBER PROCEDURE MYEXPORT( ERRORS.ERRORS_ID%TYPE));
ERRORS is a table. When I write it like above I'm getting error:
PLS-00201 - identifier must be declared.
Any clue? Thanks!
You have three issues:
The syntax is
MEMBER PROCEDURE procedure_name (argument_name data_type)
You have specified the data type but not an argument name which is why you get the error message PLS-00201 - identifier must be declared. as you need to specify an identifier for the argument.
table_name.column_name%TYPE declarations can be used in the PL/SQL scope but not in the SQL scope. Your object declaration is in the SQL scope so you need to fully define the data type rather than referring to a column.
An object must have at least one attribute.
You want:
CREATE OR REPLACE TYPE MYOBJ AS OBJECT(
attr1 NUMBER,
MEMBER PROCEDURE MYEXPORT( v_errors_id NUMBER )
);

Alter/Evolve Type User Defined Constructor Function Signature (PL/SQL)

How does one evolve a user defined constructor function signature?
Given an Oracle PL/SQL user defined type that has table dependents, instead of replacing them with create/replace statements I need to evolve these types. I found Oracle documentation on how to drop/add attributes, and drop/add member functions via alter statements, but I do not see information on how to evolve a constructor function signature. I need to do this without creating a new table/type and migrating data over to the new updated type. In my case, using FORCE does not work either.
For example, given the type below, how could I update the user defined constructor signature to include a new parameter to use during initialization?
-- Create new type
CREATE OR REPLACE TYPE test_type AS OBJECT (
test_attribute NUMBER(1, 0),
CONSTRUCTOR FUNCTION test_type(
p_test_attribute NUMBER DEFAULT NULL
)
RETURN SELF AS RESULT
);
-- Make this new type have table dependents
CREATE OR REPLACE TYPE test_type_table
AS TABLE OF test_type;
CREATE TYPE test_child_obj AS OBJECT (
test_type_field test_type_table
);
-- Add new attribute via alter statement
ALTER TYPE test_type
ADD ATTRIBUTE (new_attribute NUMBER)
CASCADE NOT INCLUDING TABLE DATA;
I would like to update the constructor signature to the following:
CONSTRUCTOR FUNCTION test_type(
p_test_attribute NUMBER DEFAULT NULL,
p_new_attribute NUMBER DEFAULT NULL
)
RETURN SELF AS RESULT
I was hoping there would be an alter statement like the following, but I cannot find a proper alter statement. Please help.
ALTER TYPE test_type
ADD CONSTRUCTOR FUNCTION test_type(
p_test_attribute NUMBER DEFAULT NULL,
p_new_attribute NUMBER DEFAULT NULL
)
RETURN SELF AS RESULT
CASCADE NOT INCLUDING TABLE DATA;
It turns out that the alter statement is in fact the one stated above and shown below. However, you can run into "too many declarations of 'test_type' match this call". In my real problem, the issue was all my constructor parameters have default NULL values and conflicted with my original constructor that all had default NULL parameters. If I take away the DEFAULT NULL, I can add new constructors.
ALTER TYPE test_type
ADD CONSTRUCTOR FUNCTION test_type(
p_test_attribute NUMBER DEFAULT NULL,
p_new_attribute NUMBER DEFAULT NULL
)
RETURN SELF AS RESULT
CASCADE NOT INCLUDING TABLE DATA;

Create object type with rowid attributes

I am trying to create a type with the rowid data type, but I am getting this error because of the type I am trying to use:
SQL> CREATE TYPE join_t IS OBJECT (inn rowid, out rowid );
/
Warning: Type created with compilation errors.
Even though I can create a table with rowid data type:
SQL> create table test_rowid (inn rowid,out rowid);
Table created.
Is it possible to create this type join_t above, with rowid-type attributes?
No, you can't create an object type with rowid fields. If you looks at the actual error raised, via the user_errors view or with the SQL*Plus command show errors, you will see:
LINE/COL ERROR
-------- ------------------------------------------------------------------------------
1/28 PLS-00530: Illegal type used for object type attribute: 'ROWID'.
1/39 PLS-00530: Illegal type used for object type attribute: 'ROWID'.
The documenation says:
Restrictions on datatype
You cannot impose the NOT NULL constraint on an attribute.
You cannot specify attributes of type ROWID, LONG, or LONG RAW.
You cannot specify a data type of UROWID for an ADT.
...
As a workaround, you could potentially use a string type in your object, and convert the values when setting to getting the field values, via the rowidtochar and chartorowid functions:
CREATE TYPE join_t IS OBJECT (inn varchar2(18), out varchar2(18) );
/
Type JOIN_T compiled
SELECT join_t(rowidtochar(rowid), rowidtochar(rowid)) FROM DUAL;
JOIN_T(ROWIDTOCHAR(ROWID),ROWIDTOCHAR(ROWID))(INN, OUT)
-------------------------------------------------------
JOIN_T('AAAAB0AABAAAAOhAAA', 'AAAAB0AABAAAAOhAAA')
Storing rowids in an object doesn't seem particularly useful though, as they can change.

Create Type based on an exiting Table

As the title said : I want to create a type in oracle based on an existing Table.
I did as follow :
create or replace type MY_NEW_TYPE as object( one_row EXISTING_TABLE%rowtype);
The Aim is to be able to use this into a function which will return a table containing sample row of the table EXISTING_TABLE :
create or replace function OUTPUT_FCT() return MY_NEW_TYPE AS
...
If you only need to create a function that returns a row from your table, you could try something like the following, without creating types.
setup:
create table EXISTING_TABLE( a number, b varchar2(100));
insert into EXISTING_TABLE values (1, 'one');
function:
create or replace function OUTPUT_FCT return EXISTING_TABLE%rowtype AS
retVal EXISTING_TABLE%rowType;
begin
select *
into retVal
from EXISTING_TABLE
where rownum = 1;
--
return retVal;
end;
function call
SQL> begin
2 dbms_output.put_line(OUTPUT_FCT().a);
3 dbms_output.put_line(OUTPUT_FCT().b);
4 end;
5 /
1
one
However, I would not recommend such an approach, because things like select * can be really dangerous; I would much prefer defining a type with the fields I need, and then explicitly query my table for the needed columns.
No, you can't do that, you'll get a compilation error:
create or replace type my_new_type as object(one_row t42%rowtype);
/
Type MY_NEW_TYPE compiled
Errors: check compiler log
show errors
Errors for TYPE STACKOVERFLOW.MY_NEW_TYPE:
LINE/COL ERROR
-------- -----------------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/36 PLS-00329: schema-level type has illegal reference to MYSCHEMA.T42
You will need to specify each field in the object type, and you will have to specify the data types manually too - you can't use table.column%type either.
You could create the type dynamically based on column and data type information from the data dictionary, but as this will (hopefully) be a one-off task and not something you'd do at runtime, that doesn't really seem worth it.
You can create a PL/SQL table type based on your table's rowtype, but you would only be able to call a function returning that from PL/SQL, not from plain SQL - so you couldn't use it in a table collection expression for example. If you were only returning a single sample row you could return a record rather than a table, but the same applies. You can also have a function that returns a ref cursor which could match the table's structure, but you wouldn't be able to treat that as a table either.
Read more about object type creation in the documentation. Specifically the attribute and datatype sections.

Oracle - PLS-00642: local collection types not allowed in SQL statements

I am new to programming in ORACLE and I am trying to compare a table column value to a passed in array and I am having a rather frustrating time in doing so.
Here is the Type Declaration from the package head.
TYPE T_STRING_ARRAY IS TABLE OF VARCHAR2(5);
and here is the the function that is using it.
create or replace PACKAGE BODY TEST_PACK IS
FUNCTION TEST_LOG_FN
(
PI_START_DATE IN VARCHAR2,
PI_END_DATE IN VARCHAR2,
PI_LOG_TYPE IN T_STRING_ARRAY
)
RETURN T_REF_CURSOR
AS
PO_RESULT T_REF_CURSOR;
BEGIN
OPEN PO_RESULT FOR
SELECT
EL.ENTRY_BASE_LOG_ID,
EL.APP_NAME,
EL.APP_MODULE,
EL.CREATION_DATE,
EL.APP_STATUS,
EL.LOG_TYPE
FROM
LG_ENTRY_BASE_LOG EL
WHERE
CREATION_DATE > PI_START_DATE AND
CREATION_DATE < PI_END_DATE AND
(EL.LOG_TYPE IN PI_LOG_TYPE OR PI_LOG_TYPE = NULL);
RETURN
PO_RESULT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END TEST_LOG_FN;
END;
The error I am getting is PLS-00642: local collection types not allowed in SQL statements. I have read online
"To avoid the PLS-00642, the collection will need to be defined at the schema level; therefore, you would need to define the varray table as a real table, using Oracle DDL with the CREATE TYPE syntax. "
http://www.dba-oracle.com/t_pls_00642_local_collection_types_not_allowed_in_sql_statement.htm
I am not sure how to do that nor have I found any references online that I could use. Can someone help me with that? If someone knows an easier way to see if a string exists in an array, that is a perfectly acceptable answer as well.
You can use types defined in the package spec in Oracle 12C or later.
This line:
(EL.LOG_TYPE IN PI_LOG_TYPE OR PI_LOG_TYPE = NULL)
Needs to be:
(EL.LOG_TYPE IN (select column_value from table(PI_LOG_TYPE))
OR (select count(*) from table(PI_LOG_TYPE)) = 0)
Prior to 12C you need to define the type in the database using CREATE TYPE. The syntax for the select is the same either way.
Rather than using IN you can use the MEMBER OF operator designed specifically for use with collections:
(PI_LOG_TYPE = NULL OR EL.LOG_TYPE MEMBER OF PI_LOG_TYPE);
As noted by #TonyAndrews If you are using Oracle 12c then you can use collections defined in a package in PL/SQL but in earlier versions you will need to define them in SQL using the CREATE TYPE statement.

Resources