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 );
Related
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
I have two table,and they are connected by one field : B_ID of table A & id of table B.
I want to use sql to insert data to this two table.
how to write the insert sql ?
1,id in table B is auto-increment.
2,in a stupid way,I can insert data to table B first,and then select the id from table B,then add the id to table A as message_id.
You cannot insert data to multiple tables in one SQL statement. Just insert data first to B table and then table A. You could use RETURNING statement to get ID value and get rid of additional select statement between inserts.
See: https://oracle-base.com/articles/misc/dml-returning-into-clause
Have you heard about AFTER INSERT trigger? I think it is what you are looking for.
Something like this might do what you want:
CREATE OR REPLACE TRIGGER TableB_after_insert
AFTER INSERT
ON TableB
FOR EACH ROW
DECLARE
v_id int;
BEGIN
/*
* 1. Select your id from TableB
* 2. Insert data to TableA
*/
END;
/
What I would like to do is;
after I insert some data into table1, I would like some of that data automatically inserted into table2, such as the primary key from table1 inserted into table2 as a foreign key.
Is this done using a trigger.
Not sure where to start looking first.
Cheers
Brian
Yes you can do that through trigger. You can do it like this:
CREATE OR REPLACE TRIGGER my_trigger
before INSERT ON table1
REFERENCING NEW AS NEW
for each row
BEGIN
insert into table2(fk_column,column1) values(:new.pk_column_of_table1,'value1');
END;
you can create a trigger as #vance said and you can use returning into clause if you are populating some of the columns dynamically
INSERT INTO t1 VALUES (t1_seq.nextval, 'FOUR')
RETURNING id INTO l_id;
have a look here
I have the following table, two types based on it, and a function that reads from this table:
CREATE TABLE myTable (
ID RAW(16) NULL,
NAME NVARCHAR2(200) NULL,
ENTITYID RAW(16) NOT NULL
);
CREATE TYPE myRowType AS OBJECT (
NAME NVARCHAR2(200),
ENTITYID RAW(16)
);
CREATE TYPE myTableType IS TABLE OF myRowType;
CREATE FUNCTION myFunction(...) RETURN myTableType ...
As you can see, the type myRowType is similar to myTable, but not exactly.
My goal is to insert rows into myTable based on the results of myFunction.
The naive approach would be to just write:
INSERT INTO myTable(ID, NAME, ENTITYID)
SELECT sys_guid(), NAME, ENTITYID
FROM TABLE(myFunction(...));
But since myFunction reads from myTable, this leads to the following error:
ORA-04091: table myTable is mutating, trigger/function may not see it
So I have to split the myFunction call from the insert statement. I tried it like this:
DECLARE
tbl myTableType;
BEGIN
SELECT myRowType(x.NAME, x.ENTITYID)
BULK COLLECT INTO tbl
FROM TABLE(myFunction(...)) x;
INSERT INTO myTable
(ID, NAME, ENTITYID)
SELECT sys_guid(), x.NAME, x.ENTITYID
FROM tbl x;
END;
But here, Oracle doesn't seem to understand the FROM tbl clause. It shows the error
ORA-00942: table or view does not exist
How can I insert the rows in tbl into myTable?
Since you can't use a locally defined nested table as an argument for TABLE function, maybe you would consider using the FORALL bulk insert? I see you are using Oracle 11g, so you will be able to access fields of myRowType. You would then replace your INSERT from your PL/SQL block with this:
FORALL v_i IN tbl.FIRST..tbl.LAST
INSERT INTO myTable VALUES (sys_guid(), tbl(v_i).name, tbl(v_i).entityid);
I recommend this great article by Tim Hall: BULK COLLECT & FORALL
I'm looking for the best way to change a data type of a column in a populated table. Oracle only allows changing of data type in colums with null values.
My solution, so far, is a PLSQL statement which stores the data of the column to be modified in a collection, alters the table and then iterates over the collection, restoring the original data with data type converted.
-- Before: my_table ( id NUMBER, my_value VARCHAR2(255))
-- After: my_table (id NUMBER, my_value NUMBER)
DECLARE
TYPE record_type IS RECORD ( id NUMBER, my_value VARCHAR2(255));
TYPE nested_type IS TABLE OF record_type;
foo nested_type;
BEGIN
SELECT id, my_value BULK COLLECT INTO foo FROM my_table;
UPDATE my_table SET my_value = NULL;
EXECUTE IMMEDIATE 'ALTER TABLE my_table MODIFY my_value NUMBER';
FOR i IN foo.FIRST .. foo.LAST
LOOP
UPDATE my_table
SET = TO_NUMBER(foo(i).my_value)
WHERE my_table.id = foo(i).id;
END LOOP;
END;
/
I'm looking for a more experienced way to do that.
The solution is wrong. The alter table statement does an implicit commit. So the solution has the following problems:
You cannot rollback after alter the alter table statement and if the database crashes after the alter table statement you will loose data
Between the select and the update users can make changes to the data
Instead you should have a look at oracle online redefinition.
Your solution looks a bit dangerous to me. Loading the values into a collection and subsequently deleting them fom the table means that these values are now only available in memory. If something goes wrong they are lost.
The proper procedure is:
Add a column of the correct type to the table.
Copy the values to the new column.
Drop the old column.
Rename the new column to the old columns name.