(Oracle) How to extract a specialized UDO from a object table based on a generalized UDO? - oracle

I apologize in advance for not knowing the terminology, but here it goes:
How do I extract a specialized UDO (UDO= user defined object & specialized= that 'extends' or is 'under' another UDO) that I inserted in a object table defined for a generalized UDO (generalized - higher in the same UDO hierarchy).
Details:
CREATE OR REPLACE TYPE GENERIC_UDO AS OBJECT (
atribute_1 TYPE,
...
atribute_n TYPE2, //TYPE2 is a nested table
) NOT FINAL NOT INSTANTIABLE;
CREATE OR REPLACE TYPE SPECIALIZED_UDO UNDER GENERIC_UDO (
atribute_1 TYPE,
...
atribute_q TYPE3, //TYPE3 is also a nested table
) FINAL INSTANTIABLE;
CREATE TABLE TBL_GENERIC_UDO OF GENERIC_UDO
( CONSTRAINT PK...)
... //other nested tables
NESTED TABLE atribute_n STORE AS atribute_n_nst;
So this works:
INSERT INTO TBL_GENERIC_UDO values (new TBL_GENERIC_UDO(...)); because TBL_GENERIC_UDO is 'under' GENERIC_UDO.
A 'select * from TBL_GENERIC_UDO' shows me as columns just the GENERIC_UDO atributes (as expected!)
So how do I extract or extract and convert, because I need to read back the inserted SPECIALIZED_UDO.
I payed a bit with:
VALUE(x): SELECT VALUE(tgo) FROM TBL_GENERIC_UDO tgo;
TREAT(x as Y): SELECT TREAT(VALUE(tgo) as SPECIALIZED_UDO) FROM TBL_GENERIC_UDO tgo;
and no good results. Actually in booth cases I get
SCHEMANAME.SPECIALIZED_UDO(values, values,..., null)
oracle.sql.STRUCT#54faa2
In the first case, when I did the insert, the corespondent value for atribute_q was set to null, in the second one it was a proper value, ex: SPECIALIZED_UDO('value1','value1',...)
I am asumming thats because TBL_GENERIC_UDO doesnt have a store clause for atribute_q from SPECIALIZED_UDO.
So anyone thoughts, explanations would be appreciated.

Solved it myself! Wasn't that hardcore as I expected. First I thought that Oracle will 'truncate' when inserting a specialized UDO into the table defined for a generic UDO, and in my case the specialized UDO had additionally another nested table as a atribute, but it doesn't.
Check code example, last line is the most important one:
CREATE OR REPLACE TYPE OBJ_TBL_TEXT IS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE OBJ_CONTAINER1 AS OBJECT (
ATRIBUTE NUMBER(6),
TBL_TEXT OBJ_TBL_TEXT
) NOT FINAL NOT INSTANTIABLE;
CREATE OR REPLACE TYPE OBJ_CONTAINER2 UNDER OBJ_CONTAINER1(
SECOND_TBL_TEXT OBJ_TBL_TEXT
) FINAL INSTANTIABLE;
CREATE TABLE TBL_CONTAINER1 OF OBJ_CONTAINER1
( CONSTRAINT PK_TBL_CONTAINER1 PRIMARY KEY(ATRIBUTE))
NESTED TABLE TBL_TEXT STORE AS TBL_TEXT_NST;
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(1,null,null));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(2,null, OBJ_TBL_TEXT('VAL1','VAL2')));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(3,OBJ_TBL_TEXT('VAL3','VAL4'), null));
INSERT INTO TBL_CONTAINER1 VALUES (NEW OBJ_CONTAINER2(4,OBJ_TBL_TEXT('VAL5','VAL6'), OBJ_TBL_TEXT('VAL7','VAL8')));
--------------------------------------------------------------------------------
SELECT * FROM TBL_CONTAINER1;
SELECT VALUE(tc) FROM TBL_CONTAINER1 tc;
SELECT TREAT(VALUE(tc) as OBJ_CONTAINER2).SECOND_TBL_TEXT FROM TBL_CONTAINER1 tc;
First select will display as columns just the atributes from the OBJ_CONTAINER1, like:
ATTRIBUTE| TBL_TEXT
--===============--
- 1 (null)
- 2 (null)
- 3 VARCHAR(VAL3,VAL4)
- 4 VARCHAR(VAL5,VAL6)
Second one looks weirder
VALUE(TC)
--=====--
DATAMODELER.OBJ_CONTAINER2(1,null,null)
oracle.sql.STRUCT#57ac8e
oracle.sql.STRUCT#1df6f
oracle.sql.STRUCT#f40801
VALUE - returns the object instance for a row of an object table as associated with the correlation_variable (table alias)
Third one lets me have access to nested attributes not mention in the nested clause in the creation of TBL_CONTAINER1.
TREAT(VALUE(tc) as OBJ_CONTAINER2).SECOND_TBL_TEXT
--==============================================--
(null)
VARCHAR(VAL1,VAL2)
(null)
VARCHAR(VAL7,VAL8)
It is exactly what I needed. Dont know yet how Oracle stores the additional attributes, and don't care. Though to share the solution...

Related

Insert records into collection type Oracle

I want to inserted into my collection some SQL records, but I can not. I'm a beginner. Can you help me ?
This is my tables :
CREATE TYPE article_type AS OBJECT (idA CHAR(10), nomA CHAR(10), prixA CHAR(10) )
CREATE TYPE facture_type AS OBJECT (idF CHAR(10), dateFact DATE)
CREATE TYPE ens_collection_fact AS OBJECT (refFact facture_type, refArticle article_type)
CREATE TYPE collection_fact AS TABLE OF ens_collection_fact
CREATE TYPE client_type AS OBJECT (idC NUMBER, nomC CHAR(10),adresse CHAR(10), Compose collection_fact )
CREATE TABLE Article OF article_type
CREATE TABLE Facture OF facture_type
CREATE TABLE Client OF client_type (PRIMARY KEY(idC)) NESTED TABLE Compose STORE AS temp
This is my query that I want to insert, but I have an error from the Oracle : ORA-02315
INSERT INTO ECOLER.CLIENT VALUES
(100, 'Jules Verne', '1', Collection_fact(Ens_collection_fact(reffact('A','2002-12-10'), ens_collection_fact(refarticle('D','E','F'))) ))
Thank in advance
reffact and refarticle are identifiers for objects within other objects, not types; you need to refer to the actual types. You also need to supply both values for each Ens_collection_fact attribute for the default constructor; you can pass null if you only want one or the other:
INSERT INTO CLIENT VALUES
(100, 'Jules Verne', '1',
Collection_fact(
Ens_collection_fact(facture_type('A',date '2002-12-10'), null),
Ens_collection_fact(null, article_type('D','E','F'))
)
)
Also notice that I've added the date keyword so it's providing an actual date literal rather than a string, which would be converted - if you're lucky - with your session NLS settings.
This will still error because 'Jules Verne' is 11 characters and you've defined the name attribute as 10 characters/bytes, but it will work with a shorter string literal.
db<>fiddle

Finding unique values from Oracle Table TYPE object

I am getting data from a feed file which is stored in Oracle Table TYPE.
Table TYPE created:
CREATE OR REPLACE TYPE typ_employee AS OBJECT
(
EMP_NAME DATE,
EMP_DEPT NUMBER,
EMP_SALARY NUMBER,
);
/
How can I find the unique records from this object and insert in another table.
Please help with this query.
You can use nested table and then use SET(Nested_table) which gives you distinct records in the nested table.
Or You can insert data from one table to another using cursor and use exception dup_val_on_index in exception block and you can skip that records (primary key should be added in second table) or update the existing one

Modify a nested table in Oracle

I have the below Nested table created :
create or replace TYPE access_t AS OBJECT (
AccessID VARCHAR2(50),
Eligibility char(1)
);
/
create or replace TYPE Access_tab IS TABLE OF access_t;
/
create or replace TYPE add_t AS OBJECT (
city VARCHAR2(100),
state VARCHAR2(100),
zip VARCHAR2(10),
APOINTSARRAY Access_tab )
;
/
create or replace TYPE add_tab IS TABLE OF add_t;
/
CREATE TABLE RQST_STATUS
( RQST_ID NUMBER,
ADDRESS add_tab
)
NESTED TABLE ADDRESS STORE AS RQST_STATUS_ADDRESS
( NESTED TABLE APOINTSARRAY STORE AS RQST_STATUS_AP)
;
If i need to change ADDRESS type to new_add_tab with some additional columns instead of add_tab , Can i just use ALTER TABLE .. MODIFY .. command ?
I am getting ORA-00922 or ORA-22913 errors . I cannot change the type directly because it is used somewhere else too. Also, the table is already loaded with data.
Please suggest.
You can do that but you have to alter the TYPE not the TABLE.
Check the documentation ALTER TYPE Statement: alter_method_spec
Most important is the CASCADE key word.
Examples:
ALTER TYPE access_t ADD ATTRIBUTE NEW_Eligibility INTEGER CASCADE;
ALTER TYPE access_t DROP ATTRIBUTE Eligibility CASCADE;
ALTER TYPE access_t MODIFY ATTRIBUTE AccessID VARCHAR2(100) CASCADE;
Here is a step-by-step description of my suggestion. It might not be the most elegant, but I think that it would be best for you to have something you can fully understand (as opposed to an obscure trick).
Also, and since I don't really know what kind of changes you need to do for the internal table, I'm leaving the maximal flexibility for you to do any change you may wish to do.
Let's call your table T1 that contains a columns C_T which is your internal table.
The internal table contains columns C_1, C-2 and C_3, and you want the new structure for the record to be D_1, D_2, D_3, D_4 and D_5, where the mapping is:
C_1 -> D_5,
C_2 -> D_1,
C_3 -> D_2,
{new} -> D_3,
{new} -> D_4.
Create a tempo table TEMPO_T with a column SOURCE_ROWID (varchar2(64)) and the new columns D_1,..., D5.
Write a small anonymous block having a cursor that selects the ROWID of each row of table T1 and all the records within the internal table in column C_T (order by ROWID). The result would look like (this is just an example of course):
ROWID C_1 C_2 C_2
wwereeedffff 1 a ww
wwereeedffff 2 b xx
wwereeedffff 7 l yy
ertrtrrrtrrr 5 d PP
ertrtrrrtrrr 99 h mm
...
[Note: The use of ROWID is under the assumption that you don't have a column that can serve as a unique identifier for each row in table T1; if there is such column - one defined as UNIQUE INDEX - you can use that field instead]
Having this query ready, convert it into an INSERT into the temporary table TEMPO_T along with whatever values you need to store for columns D_3 and D_4.
Now, you have a backup of the original contents of column C_T and hence can delete the column.
Now, you can update the type that defines the structure of column C_T to its new form (i.e. D_1,...,D_5) and alter table T1 by adding a column whose type is the updated one.
Finally, you can insert the contents of column C_T with that stored in the temporary table (since you already have this, I assume that you know how to implement it - inserting a table within a cell column of the outer table).
That's it.
Needless to say, I would make a backup of your data before engaging into this.
Hope this description is detailed enough to enable you to complete the task at hand.

How to Copy data from table with a nested table column

I have a table that has one of its columns as a nested table.
I want to copy data of this table to another. How do we frame the INSERT clause for this seems challenging:
Consider. The field that is a nested table is phone_list whose type is a user defined type "TBL_PHONE_EXTN" which is a table of "typ_phone_extn".
CREATE OR REPLACE TYPE typ_phone_extn AS OBJECT
(phone_number VARCHAR2 (20), extension VARCHAR2 (10));
/
CREATE OR REPLACE TYPE tbl_phone_extn AS TABLE OF typ_phone_extn;
/
Obviously below fails: (with a ORA-00904: : invalid identifier)
INSERT INTO sch2.sub_pat_address (
pat_address_id,
pat_id,
**phone_list,**
last_updated_by
)
SELECT pat_address_id,
pat_id,
**phone_list,**
last_updated_by
FROM sch1.sub_pat_address ;
So i try:
SELECT pat_address_id,
pat_id,
**tbl_phone_extn(typ_phone_extn (phone_number,extension)),**
last_updated_by
FROM sch1.sub_pat_address, **table(phone_list)** ;
What this does is unnest the nested table. So i end up with more records than i want - meaning if a specific pat_address_id had a phone_list of 5 phone,extn combination this gives me 5 records that i cannot and should not be inserting.
So question is, how to keep the nest (nested table column) as-is and insert into the new table? Well, CTAS may be one option but that requires a whole new table instead of a INSERT. Any help will be greatly appreciated.
You can use the COLLECT function to reassemble the unnested elements into a nested table, casting that back to your actual collection type:
SELECT pat_address_id,
pat_id,
cast(collect(typ_phone_extn(phone_number,extension)) as tbl_phone_extn),
last_updated_by
FROM sch1.sub_pat_address, table(phone_list)
GROUP BY pat_address_id, pat_id, last_updated_by;
And you can then use that for your insert, obviously.
The only reason I can see you'd have a problem with your original simple insert would be if each schema had its own types and their tables were built using their own types. But then you'd get ORA-00932: inconsistent datatypes or ORA-01031: insufficient privileges rather than ORA-00904.
Even if you have privileges on the types across the schemas, Oracle UDTs have to be the exact same type - it's not enough for them to be constructed identically. If they are different entries in ALL_OBJECTS then they are not interchangeable.

Inserting an empty row

This is so simple it has probably already been asked, but I couldn't find it (if that's the case I'm sorry for asking).
I would like to insert an empty row on a table so I can pick up its ID (primary key, generated by an insert trigger) through an ExecuteScalar. Data is added to it at a later time in my code.
My question is this: is there a specific insert syntax to create an empty record? or must I go with the regular insert syntax such as "INSERT INTO table (list all the columns) values (null for every column)"?
Thanks for the answer.
UPDATE: In Oracle, ExecuteScalar on INSERT only returns 0. The final answer is a combination of what was posted below. First you need to declare a parameter, and pick up it up with RETURNING.
INSERT INTO TABLENAME (ID) VALUES (DEFAULT) RETURNING ID INTO :parameterName
Check this out link for more info.
You would not have to specify every single column, but you may not be able to create an "empty" record. Check for NOT NULL constraints on the table. If none (not including the Primary Key constraint), then you would only need to supply one column. Like this:
insert into my_table ( some_column )
values ( null );
Do you know about the RETURNING clause? You can return that PK back to your calling application when you do the INSERT.
insert into my_table ( some_column )
values ( 'blah' )
returning my_table_id into <your_variable>;
I would question the approach though. Why create an empty row? That would/could mean there are no constraints on that table, a bad thing if you want good, clean, data.
Basically, in order to insert a row where values for all columns are NULL except primary
key column's value you could execute a simple insert statement:
insert into your_table(PK_col_name)
values(1); -- 1 for instance or null
The before insert trigger, which is responsible for populating primary key column will
override the value in the values clause of the insert statement leaving you with an
empty record except PK value.

Resources