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