How to Copy data from table with a nested table column - oracle

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.

Related

Unable to create table through another table in oracle SQL developer

I have been trying to create a new table by using below query :
"Create table d1_details_test2
as
select * from d1_details"
this above query gives me an error :
actually "d1_details" table has one column which has "Long" datatype and i cannot change it.
so i want to know the any other way to create the table.
Thanks
The long data type is subject to many restrictions. Create table as select is one of these.
You can get around it by applying to_lob in the select, which converts it to a clob:
create table views as
select view_name, text from user_views;
ORA-00997: illegal use of LONG datatype
create table views as
select view_name, to_lob ( text ) lob
from user_views;
desc views
Name Null? Type
VIEW_NAME VARCHAR2(128)
LOB CLOB

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

plsql table type with index of is complaining

Pl/SQL:
Intent: My intent was to access employee tuple object defied as cursor below by using key as the employee_id.
Problem: I created a cursor - *l_employees_cur* and want to create type table as below type *l_employees_t*, as below but the compiler is complaining saying that PLS-00315 implementation restriction unsupported table index type.
CURSOR l_employees_cur
IS
SELECT employee_id,manager_id,first_name,last_name FROM employees;
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY employees.employee_id%TYPE;
The definition of employees.employee_id is:
EMPLOYEE_ID NUMBER(6) NOT NULL
why can't I do this ? or Am I doint something wrong.
From the Oracle Documenation:
Associative Arrays
An associative array (formerly called PL/SQL table or index-by table) is a set of key-value pairs. Each key is a unique index, used to locate the associated value with the syntax variable_name(index).
The data type of index can be either a string type or PLS_INTEGER. Indexes are stored in sort order, not creation order. For string types, sort order is determined by the initialization parameters NLS_SORT and NLS_COMP.
I think that your mistake is the declaration of the plsql table.
Why don't you try the next one:
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY pls_integer;
I also have a question for you:
What is the meaning of EMPLOYEE_ID NOT NULL NUMBER(6) in your code above?
Greetings
Carlos
Storing and Retreiving SQL Query Output in a PL/SQL Collection
The example in the OP looks a lot like Oracle's new sample HR data schema. (For those old-timers who know, the successor to the SCOTT-TIGER data model). This solution was developed on an Oracle 11g R2 instance.
The Demo Table Design - EMP
Demonstration Objectives
This example will show how to create a PL/SQL collection from an object TYPE definition. The complex data type is derived from the following cursor definition:
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
After loading the cursor contents into an index-by collection variable, the last half of the stored procedure contains an optional step which loops back through the collection and displays the data either through DBMS_OUTPUT or an INSERT DML operation on another table.
Stored Procedure Example Source Code
This is the stored procedure used to query the demonstration table, EMP.
create or replace procedure zz_proc_employee is
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
TYPE employees_tbl_type IS TABLE OF l_employees_cur%ROWTYPE INDEX BY PLS_INTEGER;
employees_rec_var l_employees_cur%ROWTYPE;
employees_tbl_var employees_tbl_type;
v_output_string varchar2(80);
c_output_template constant varchar2(80):=
'Employee: <<EMP>>; Manager: <<MGR>>; Employee Name: <<ENAME>>';
idx integer;
outloop integer;
BEGIN
idx:= 1;
OPEN l_employees_cur;
FETCH l_employees_cur INTO employees_rec_var;
WHILE l_employees_cur%FOUND LOOP
employees_tbl_var(idx):= employees_rec_var;
FETCH l_employees_cur INTO employees_rec_var;
idx:= idx + 1;
END LOOP;
CLOSE l_employees_cur;
-- OPTIONAL (below) Output Loop for Displaying The Array Contents
-- At this point, employees_tbl_var can be handed off or returned
-- for additional processing.
FOR outloop IN 1 .. idx LOOP
-- Build the output string:
v_output_string:= replace(c_output_template, '<<EMP>>',
to_char(employees_tbl_var(outloop).employee_id));
v_output_string:= replace(v_output_string, '<<MGR>>',
to_char(employees_tbl_var(outloop).manager_id));
v_output_string:= replace(v_output_string, '<<ENAME>>',
employees_tbl_var(outloop).last_name);
-- dbms_output.put_line(v_output_string);
INSERT INTO zz_output(output_string, output_ts)
VALUES(v_output_string, sysdate);
COMMIT;
END LOOP;
END zz_proc_employee;
​
I commented out the dbms_output call due to problems with the configuration of my server beyond my control. The alternate insert command to a output table is a quick way of visually verifying that the data from the EMP table found its way successfully into the declared collection variable.
Results and Discussion of the Solution
Here is my output after calling the procedure and querying my output table:
While the actual purpose behind the access to this table isn't clear in the very terse detail of the OP, I assumed that the first approach was an attempt to understand the use of collections and custom data types for efficient data extraction and handling from structures such as PL/SQL cursors.
The portion of this example procedure is very reusable, and the initial steps represent a working way of making and loading PL/SQL collections. If you notice, even if your own version of this EMP table is different, the only place that requires redefinition is the cursor itself.
Working with types, arrays, nested tables and other collection types will actually simplify work in the long run because of their dynamic nature.

Oracle Datatype Modifier

I need to be able to reconstruct a table column by using the column data in DBA_TAB_COLUMNS, and so to develop this I need to understand what each column refers to. I'm looking to understand what DATA_TYPE_MOD is -- the documentation (http://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_2094.htm#I1020277) says it is a data type modifier, but I can't seem to find any columns with this field populated or any way to populate this field with a dummy column. Anyone familiar with this field?
Data_type_mod column of the [all][dba][user]_tab_columns data dictionary view gets populated when a column of a table is declared as a reference to an object type using REF datatype(contains object identifier(OID) of an object it points to).
create type obj as object(
item number
) ;
create table tb_1(
col ref obj
)
select t.table_name
, t.column_name
, t.data_type_mod
from user_tab_columns t
where t.table_name = 'TB_1'
Result:
table_name column_name data_type_mod
-----------------------------------------
TB_1 COL REF
Oracle has a PL/SQL package that can be used to generate the DDL for creating a table. You would probably be better off using this.
See GET_DDL on http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_metada.htm#i1019414
And see also:
How to get Oracle create table statement in SQL*Plus

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

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...

Resources