Select into multilevel type in Oracle - oracle

I am trying to find out if there is a way to bulk collect into a multi-level type in Oracle. The example below should help in explaining the concept of what I am trying to do.
There is a source table with a denormalised list of counties and towns:
create table county_town (county varchar2(20), town varchar2(20));
insert into county_town values ('Surrey', 'Dorking');
insert into county_town values ('Surrey', 'Woking');
insert into county_town values ('Surrey', 'Guildford');
insert into county_town values ('Oxfordshire', 'Thame');
insert into county_town values ('Oxfordshire', 'Abingdon');
What I want to do is load this into a multilevel type that looks like this:
create type towns_typ as table of varchar2(20);
create type counties_typ as object (country varchar2(20), towns towns_type);
create type nt_counties_typ as table of counties_typ;
l_county_data nt_counties_typ
Is there some way that I can write a SELECT statement to BULK collect this data into l_county_data from the table county_town ? If BULK COLLECT cant be used is there another way to do this simply?

Yes, like here:
declare
l_county_data nt_counties_typ;
begin
select counties_typ(county, cast(collect(town) as towns_typ))
bulk collect into l_county_data
from county_town
group by county;
dbms_output.put_line(l_county_data(2).county);
end;
dbfiddle

Related

PLSQL : Insert Result from Cursor into one column of plsql table

The following is my code To create a table object :
TYPE TempObjectsTable IS TABLE OF t_temp_objects%ROWTYPE
INDEX BY BINARY_INTEGER;
nt_scb_temp_objects TempObjectsTable;
The t_temp_objects has the following Columns defined :
Name Null? Type
-------------- ----- -------------
INVC_REF NUMBER
ORDERS NUMBER
ORDER_POS_TYPE NUMBER
RULE_CONDITION VARCHAR2(500)
CHARGE NUMBER
CURRENCY VARCHAR2(10)
TXN_DT DATE
Now, I have a cursor, which returns a lists of Orders, basically numbers.
CURSOR c_orders_frm_grp IS
select a.ordr_id from sa_order a
WHERE a.invc_ref is NULL
I am trying to add these to the plsql table created nt_scb_temp_objects above by using bulk collect. But i want the rest of the columns of nt_scb_temp_objects to filled as null for now, as i will be filling these columns as well in the coming steps.
Currently this is what i am trying.
IF c_orders_frm_grp %ISOPEN THEN
CLOSE c_orders_frm_grp ;
END IF;
OPEN c_orders_frm_grp;
FETCH c_orders_frm_grp BULK COLLECT INTO nt_scb_temp_objects.orders;
CLOSE c_orders_frm_grp;
And this is the error i get : Error(44,74): PLS-00302: component 'ORDERS' must be declared
You do not want that CURSOR and OPEN..FETCH constructs. Simply run a SELECT BULK COLLECT INTO
that collection.
DECLARE
TYPE TempObjectsTable IS TABLE OF t_temp_objects%ROWTYPE
INDEX BY BINARY_INTEGER;
nt_scb_temp_objects TempObjectsTable;
BEGIN
select a.ordr_id as ORDERS,
null as INVC_REF,
null as ORDER_POS_TYPE,
null as RULE_CONDITION,
null as CHARGE,
null as CURRENCY,
null as TXN_DT
BULK COLLECT INTO nt_scb_temp_objects from sa_order a
WHERE a.invc_ref is NULL ;
END;
/
DEMO
Why not use an INSERT INTO ... SELECT, and only specify the single column you want to populate now:
INSERT INTO TempObjectsTable(ORDERS)
SELECT ordr_id
FROM sa_order
WHERE invc_ref IS NULL;
In general you should avoid using cursors, as most regular database operations in SQL are already set based.
Note: If the temp table TempObjectsTable does not already exist, then you will have to create it.

Oracle: insert from type table

i want to insert from a type of table into a table.
Is there a way to do this with bulk? And can I change the type table content a little?
Just like here, but the other way around:
How to insert data into a PL/SQL table type rather than PL/SQL table?
Assuming that you have something like
CREATE TYPE my_nested_table_type
AS TABLE OF <<something>>;
DECLARE
l_nt my_nested_table_type;
BEGIN
<<something that populates l_nt>>
then the way to do a bulk insert of the data from the collection into a heap-organized table would be to use a FORALL
FORALL i in 1..l_nt.count
INSERT INTO some_table( <<list of columns>> )
VALUES( l_nt(i).col1, l_nt(i).col2, ... , l_nt(i).colN );

Oracle Insert Data Into Object Table

What is the most per-formant manner to insert data into an object table?
Create Type R_Emp Is Object
(Emp_Id number, Last_Name varchar(50));
create type T_Emp is table of R_Emp;
Then given an inbound array insert values:
V_T_Emp T_Emp := T_Emp();
For i In 1..p_array.COUNT
Loop
..... // Best way to load values
Where is the data coming from? Assuming the data is coming from one or more tables
SELECT r_emp( emp_id, last_name )
BULK COLLECT INTO v_t_emp
FROM table_name
If the data is coming from somewhere else, you'll need to tell us where the data is coming from. If p_array and v_t_emp are both collections of type t_emp, you can just directly assign them
v_t_emp := p_array;
But if you're just copying the data from one collection to another identically defined collection, there normally wouldn't be any reason to introduce the second collection.

Using objects to insert variable array into nested table

I have a series of tables and objects I have defined. I have an object nested table that I am trying to insert values into. The values are in the form of a variable array but I don't know how to insert them. my tables and code are as follows.
Table wu.classes
crn number(5)
department varchar2(8)
title carchar2(25)
Table wu.students
student_id char(11)
name varchar2(10)
dept varchar2(8)
advisor varchar(10)
classes wu.classes_va
wu.classes_va varray(5) of number (5)
create type classes_ty as object(crn varchar2(5),department varchar2(8), coursetitle varchar2(25)
create table classes_ot of classes_ty;
insert into classes_ot select crn,department,title from wu.classes;
create or replace type classes_ref_ty as table of ref classes_ty;
create table student_plus(student# varchar2(11),student_name varchar2(10),major varchar2(8), advisor (10), enrolled classes_ref_ty) nested table enrolled store as classes_ref_ty_tab;
Problem here (I need to loop through to fill the table but I just need to know how to do it for one values and i can figure the rest out):
begin
insert into student_plus values('700-123-948','Hooker','CS','VanScoy',classes_ref_ty();
insert into table(select enrolled from student_plus where student#='700-123-948')
select ref(c) from classes_ot c where ???
end;
/
I don't know how to access the variable array and use it with the classes_ref_ty.

Keeping track of all values created by a sequence for multiple inserts

In PL SQL, I'm writing a stored procedure that uses a DB link:
CREATE OR REPLACE PROCEDURE Order_Migration(us_id IN NUMBER, date_id in DATE)
as
begin
INSERT INTO ORDERS(order_id, company_id)
SELECT ORDER_ID_SEQ.nextval, COMPANY_ID
FROM ORDERS#SOURCE
WHERE USER_ID = us_id AND DUE_DATE = date_ID;
end;
It takes all orders done on a certain day, by a certain user and inserts them in the new database. It calls a sequence to makes sure there are no repeat PKs on the orders, and it works well.
However, I want the same procedure to do a second INSERT into another table that has order_id as a foreign key. So I need to add all the order_id's just created, and the data from SOURCE that matches:
INSERT INTO ORDER_COMPLETION(order_id, completion_dt)
SELECT ????, completion_dt
FROM ORDER_COMPLETION#SOURCE
How can I keep track of which order_id that was just created matches up to the one whose data I need to pull from the source database?
I looked into making a temporary table, but you can't create those in a procedure.
Other info: I'll be calling this procedure from a C# app I'm writing
I'm not sure that I follow the question. If there is an ORDERS table and an ORDER_COMPLETION table in the remote database, wouldn't there be some key on the source system that related those two tables? If that key is the ORDER_ID, why would you want to re-assign that key in your procedure? Wouldn't you want to maintain the ORDER_ID from the source system?
If you do want to re-assign the ORDER_ID locally, I would tend to think that you'd want to do something like
CREATE OR REPLACE PROCEDURE order_migration( p_user_id IN orders.user_id%type,
p_due_date IN orders.due_date%type )
AS
TYPE order_rec IS RECORD( new_order_id NUMBER,
old_order_id NUMBER,
company_id NUMBER,
completion_dt DATE );
TYPE order_arr IS TABLE OF order_rec;
l_orders order_arr;
BEGIN
SELECT order_id_seq.nextval,
o.order_id,
o.company_id,
oc.completion_dt
BULK COLLECT INTO l_orders
FROM orders#source o,
order_completion#source oc
WHERE o.order_id = oc.order_id
AND o.user_id = p_user_id
AND o.due_date = p_due_date;
FORALL i IN l_orders.FIRST .. l_orders.LAST
INSERT INTO orders( order_id, company_id )
VALUES( l_orders(i).new_order_id, l_orders(i).company_id );
FORALL i IN l_orders.FIRST .. l_orders.LAST
INSERT INTO order_completion( order_id, completion_dt )
VALUES( l_orders(i).new_order_id, l_orders(i).completion_dt );
END;
You could also do a single FOR loop with two INSERT statements rather than two FORALL loops. And if you're pulling a lot of data each time, you probably want to pull the data in chunks from the remote system by adding a loop and a LIMIT to the BULK COLLECT
There must be some link between the rows in ORDERS#SOURCE and ORDERS, and between ORDERS#SOURCE and ORDER_COMPLETION#SOURCE, so can you not use a join?
Something like:
INSERT INTO ORDER_COMPLETION(order_id, completion_dt)
SELECT o.order_id, ocs.completion_dt
FROM ORDER_COMPLETION#SOURCE ocs
JOIN ORDERS o ON o.xxx = ocs.xxx

Resources