Multiple insert SQL oracle - sql

How do you do multiple insert with SQL in Oracle 12c when you have an identity column?
INSERT ALL
INTO Table1 (Column2) Values (1)
INTO Table1 (Column2) Values (2)
SELECT * FROM dual;
where Table1 has column1 as an identity, will set the identity column to have the same value which violates the primary key constraint.
CREATE TABLE Table1 (
Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
column2 VARCHAR2(255),
column3 NUMBER,
PRIMARY KEY (Table1Id)
);
INSERT ALL
INTO Table1 (column2, column3) VALUES ('a', '1')
INTO Table1 (column2, column3) VALUES ('b', '2')
SELECT * FROM dual;
--SQL Error: ORA-00001: unique constraint violated
What am I doing wrong with this?

EDIT Added two test cases, and a possible workaround.
Though Insert statement and insert all statement are practically the same conventional insert statement. But when it comes to sequences, they work differently.
Test case 1 : Identity columns
SQL> DROP TABLE table1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 PRIMARY KEY (Table1Id)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES ('1')
3 INTO Table1 (column3) VALUES ('2')
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.SYS_C0010439) violated
SQL>
Let's see what's actually happening under the hood -
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 CONSTRAINT A UNIQUE (Table1Id)
5 );
Table created.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.A) violated
SQL> SELECT * FROM table1;
no rows selected
SQL> ALTER TABLE table1
2 DISABLE CONSTRAINT a;
Table altered.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
2 rows created.
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
2 2
SQL>
So, the sequence progressed to nextval however there was an unique constraint violation the first time we did an Insert All. Next, we disabled the unique constraint, and the subsequent Insert All reveals that the sequence did not progress to nextval, rather it attempted to insert duplicate keys.
Though the issue doesn't occur with a INSERT-INTO-SELECT statement.
SQL> INSERT INTO table1(column3) SELECT LEVEL FROM dual CONNECT BY LEVEL <=5;
5 rows created.
SQL>
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
3 2
4 3
5 4
6 5
SQL>
Surprisingly, as per the metadata, the sequence is supposed to proceed to nextval automatically, however it doesn't happen with an Insert All statement.
SQL> SELECT COLUMN_NAME,
2 IDENTITY_COLUMN,
3 DATA_DEFAULT
4 FROM user_tab_cols
5 WHERE table_name ='TABLE1'
6 AND IDENTITY_COLUMN='YES';
COLUMN_NAME IDENTITY_COLUMN DATA_DEFAULT
--------------- --------------- ------------------------------
TABLE1ID YES "LALIT"."ISEQ$$_94458".nextval
SQL>
Test Case 2 : Using a sequence explicitly
The INSERT ALL would work the same way whether an identity column is used or an explicit sequence is used.
SQL> DROP SEQUENCE s;
Sequence dropped.
SQL>
SQL> CREATE SEQUENCE s;
Sequence created.
SQL>
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t (
2 ID NUMBER,
3 text VARCHAR2(50),
4 CONSTRAINT id_pk PRIMARY KEY (ID)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.ID_PK) violated
SQL>
SQL> SELECT * FROM T;
no rows selected
SQL>
SQL> ALTER TABLE t
2 DISABLE CONSTRAINT id_pk;
Table altered.
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM T;
ID TEXT
---------- ----------------------------------------
2 a
2 b
2 c
2 d
SQL>
Possible workaround - Using a ROW LEVEL trigger
SQL> CREATE OR REPLACE TRIGGER t_trg
2 BEFORE INSERT ON t
3 FOR EACH ROW
4 WHEN (new.id IS NULL)
5 BEGIN
6 SELECT s.NEXTVAL
7 INTO :new.id
8 FROM dual;
9 END;
10 /
Trigger created.
SQL> truncate table t;
Table truncated.
SQL> INSERT ALL
2 INTO t (text) VALUES ('a')
3 INTO t (text) VALUES ('b')
4 INTO t (text) VALUES ('c')
5 INTO t (text) VALUES ('d')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM t;
ID TEXT
---------- -------------------------
3 a
4 b
5 c
6 d
SQL>

Here's a workaround using the UNION ALL method instead of the INSERT ALL method. For some reason the data must be wrapped in a select * from (...) or it will generate the error ORA-01400: cannot insert NULL into ("JHELLER"."TABLE1"."TABLE1ID").
insert into table1(column2, column3)
select *
from
(
select 'a', '1' from dual union all
select 'b', '2' from dual
);

Related

Delete specific record from multiple child table(Not all child table) and then the parent table entry [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 10 months ago.
Improve this question
I have a parent table name A and there are 47 child table which are referencing to this parent table A, i want to delete records from 23 child tables and then the parent table as well, and want to keep the records in the remaining child tables.
I tried doing this by disabling and enabling constraint but faced issue while enabling the constraint.
Can anyone suggest any better approach.
NOTE: I am trying to achieve this via shell script.
Keyword here is ENABLE NOVALIDATE. It'll let you keep "invalid" existing data, but referential integrity will be enforced for newly added rows.
Here's an example.
Master and two detail tables:
SQL> create table master
2 (id_mas number primary key,
3 name varchar2(20));
Table created.
SQL> create table det_1
2 (id_det number primary key,
3 id_mas number constraint fk_d1_mas references master (id_mas),
4 name varchar2(20));
Table created.
SQL> create table det_2
2 (id_det number primary key,
3 id_mas number constraint fk_d2_mas references master (id_mas),
4 name varchar2(20));
Table created.
SQL>
Sample rows:
SQL> insert into master (id_mas, name)
2 select 1, 'Little' from dual union all
3 select 2, 'Foot' from dual;
2 rows created.
SQL> insert into det_1 (id_det, id_mas, name)
2 select 100, 1, 'Lit det 1' from dual union all
3 select 101, 1, 'Tle det 1' from dual union all
4 select 102, 2, 'Foot det 1' from dual;
3 rows created.
SQL> insert into det_2 (id_det, id_mas, name)
2 select 200, 1, 'Lit det 2' from dual union all
3 select 201, 2, 'Tle det 2' from dual union all
4 select 202, 2, 'Foot det 2' from dual;
3 rows created.
SQL> commit;
Commit complete.
SQL>
Now, let's delete ID_MAS = 1 from DET_1 and MASTER, but keep it in DET_2:
Deleting from detail table is OK (why wouldn't it be?):
SQL> delete from det_1 where id_mas = 1;
2 rows deleted.
I can't delete from master table because of foreign key constraint from DET_2:
SQL> delete from master where id_mas = 1;
delete from master where id_mas = 1
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_D2_MAS) violated - child record found
So, let's disable it:
SQL> alter table det_2 disable constraint fk_d2_mas;
Table altered.
Deleting from master now succeeds:
SQL> delete from master where id_mas = 1;
1 row deleted.
Re-enabling previously disabled constraint on DET_2 will fail because it contains row(s) whose ID_MAS doesn't exist in MASTER table any more:
SQL> alter table det_2 enable constraint fk_d2_mas;
alter table det_2 enable constraint fk_d2_mas
*
ERROR at line 1:
ORA-02298: cannot validate (SCOTT.FK_D2_MAS) - parent keys not found
As I said, use ENABLE NOVALIDATE:
SQL> alter table det_2 enable novalidate constraint fk_d2_mas;
Table altered.
Tables' contents:
SQL> select * from master;
ID_MAS NAME
---------- --------------------
2 Foot
SQL> select * from det_1;
ID_DET ID_MAS NAME
---------- ---------- --------------------
102 2 Foot det 1
SQL> select * from det_2;
ID_DET ID_MAS NAME
---------- ---------- --------------------
200 1 Lit det 2 --> this master doesn't exist any more
201 2 Tle det 2
202 2 Foot det 2
SQL>
Let's try to insert some new (valid and invalid) rows:
SQL> insert into det_1 (id_det, id_mas, name)
2 select 110, 2, 'Valid' from dual;
1 row created.
SQL> insert into det_1 (id_det, id_mas, name)
2 select 111, 1, 'Invalid' from dual;
insert into det_1 (id_det, id_mas, name)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_D1_MAS) violated - parent key not
found
SQL> insert into det_2 (id_det, id_mas, name)
2 select 210, 2, 'Valid' from dual;
1 row created.
SQL> insert into det_2 (id_det, id_mas, name)
2 select 211, 1, 'Invalid' from dual;
insert into det_2 (id_det, id_mas, name)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_D2_MAS) violated - parent key not
found
SQL>
I am trying to achieve this via shell script.
A shell script? What does shell have to do with that? It is Oracle's business.

Insert into table requires specific condition from another table

I have two SQL tables with the following schema:
road_test (test_ID, examiner_ID, student_ID, vin, test_date)
lessons_count (student_ID, lessons_taken)
I am looking for some way to require a student to have at least 5 lessons_taken before they can insert into the road_test table.
Is there some sort of trigger or constraint that allows for this?
Don't store count of lessons; calculate it whenever needed.
Here's what I'd suggest:
SQL> -- the final table
SQL> create table road_test
2 (test_id number, student_id number, vin number);
Table created.
SQL> -- table that shows which lessons were taken by which student
SQL> create table lesson
2 (student_id number, lesson_id number);
Table created.
SQL>
Trigger which is supposed to control whether you're allowed to insert student's record into the road_test table: count number of lessons taken and raise an error if it is too low (I set it to 3 for simplicity):
SQL> create or replace trigger trg_bi_road
2 before insert on road_test
3 for each row
4 declare
5 l_cnt number;
6 begin
7 select count(*)
8 into l_cnt
9 from lesson
10 where student_id = :new.student_id;
11 if l_cnt < 3 then
12 raise_application_error(-20001,
13 'You have to take at least 3 lessons');
14 end if;
15 end;
16 /
Trigger created.
SQL>
Testing (as I said: restricted to 3 lessons, for simplicity):
SQL> -- initial record
SQL> insert into lesson(student_id, lesson_id) values (1, 100);
1 row created.
SQL> -- can I enter that student into the ROAD_TEST table? Nope
SQL> insert into road_test (test_id, student_id, vin) values (555, 1, 123456);
insert into road_test (test_id, student_id, vin) values (555, 1, 123456)
*
ERROR at line 1:
ORA-20001: You have to take at least 3 lessons
ORA-06512: at "SCOTT.TRG_BI_ROAD", line 9
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_ROAD'
SQL> -- Let's insert 2 more lessons for the same student
SQL> insert into lesson(student_id, lesson_id) values (1, 200);
1 row created.
SQL> insert into lesson(student_id, lesson_id) values (1, 300);
1 row created.
SQL> -- New attempt for the ROAD_TEST table:
SQL> insert into road_test (test_id, student_id, vin) values (555, 1, 123456);
1 row created.
SQL> select * From lesson;
STUDENT_ID LESSON_ID
---------- ----------
1 100
1 200
1 300
SQL> select * from road_test;
TEST_ID STUDENT_ID VIN
---------- ---------- ----------
555 1 123456
SQL>

Store data from table into object table

How would you go about transferring data into an object table?
Say you have a table:
create table thisTable(
column1 varchar2(20),
column2 varchar2(20),
column3 varchar2(20)
)
/
And you have a new object table:
create table oo_thisTable(
object1 object1_t
)
/
create type object1_t as object (
column1 varchar2(20),
column2 varchar2(20),
column3 varchar2(20)
)
/
How would you transfer the data from thisTable to oo_thisTable?
declare
cursor c1 is
select * from thisTable;
begin
open c1;
loop
fetch c1 into column1, column2, column3;
exit when c1%notfound;
...
No need of using PL/SQL, you could do it in pure SQL.
INSERT INTO oo_thistable SELECT object1_t(column1, column2, column3) FROM thistable;
Demo
Create the required type and tables:
SQL> create table thisTable(
2 column1 varchar2(20),
3 column2 varchar2(20),
4 column3 varchar2(20)
5 )
6 /
Table created.
SQL> create type object1_t as object (
2 column1 varchar2(20),
3 column2 varchar2(20),
4 column3 varchar2(20)
5 )
6 /
Type created.
SQL> create table oo_thisTable(
2 object1 object1_t
3 )
4 /
Table created.
Insert few rows in thistable:
SQL> INSERT INTO thistable VALUES('a','b','c');
1 row created.
SQL> INSERT INTO thistable VALUES('d','e','f');
1 row created.
SQL> INSERT INTO thistable VALUES('g','h','i');
1 row created.
Now we want to insert all rows from thistable into oo_thistable:
SQL> INSERT INTO oo_thistable SELECT object1_t(column1, column2, column3) FROM thistable;
3 rows created.
Validate:
SQL> SELECT * FROM oo_thistable;
OBJECT1(COLUMN1, COLUMN2, COLUMN3)
--------------------------------------------------------------------------------
OBJECT1_T('a', 'b', 'c')
OBJECT1_T('d', 'e', 'f')
OBJECT1_T('g', 'h', 'i')
You have all the rows inserted.
INSERT INTO oo_thisTable (object1) SELECT object1_t(column1, column2, column3) FROM thisTable;
For better readability you can use keyword NEW to show explicitly that you invoke type constructor not just some fuction.
INSERT INTO oo_thisTable (object1) SELECT new object1_t(column1, column2, column3) FROM thisTable;
I have written some sequential steps for the issue described. Please try this and let me know if this helps.
SQL> set sqlbl on;
SQL> set define off;
SQL> set timing on;
SQL> DROP TYPE TEST_TAB;
Type dropped.
Elapsed: 00:00:00.90
SQL>
SQL> DROP TYPE Test_oo;
Type dropped.
Elapsed: 00:00:00.58
SQL> CREATE OR REPLACE type Test_oo
2 IS
3 OBJECT
4 (
5 col1 NUMBER,
6 COL2 VARCHAR2(100 CHAR),
7 COL3 TIMESTAMP
8 );
9 /
Type created.
Elapsed: 00:00:00.21
SQL> CREATE OR REPLACE TYPE TEST_TAB IS TABLE OF TEST_OO;
2 /
Type created.
Elapsed: 00:00:00.20
SQL> DROP TABLE TEST_TABLE;
Table dropped.
Elapsed: 00:00:00.39
SQL> CREATE TABLE TEST_TABLE
2 (
3 O_col1 NUMBER,
4 O_COL2 VARCHAR2(100 CHAR),
5 O_COL3 TIMESTAMP
6 );
Table created.
Elapsed: 00:00:00.28
SQL> INSERT INTO TEST_TABLE
2 SELECT LEVEL,LEVEL||'AV',SYSDATE+LEVEL
3 FROM DUAL
4 CONNECT BY LEVEL < 10;
9 rows created.
Elapsed: 00:00:00.11
SQL>
SQL> COMMIT;
Commit complete.
Elapsed: 00:00:00.10
SQL> DECLARE
2 lv_obj TEST_TAB;
3 BEGIN
4 dbms_output.put_line('test');
5 SELECT test_oo(T1.O_COL1,T1.O_COL2,T1.O_COL3) bulk collect
6 INTO lv_obj
7 FROM test_table t1;
8 FOR I IN LV_OBJ.FIRST..LV_OBJ.LAST
9 LOOP
10 dbms_output.put_line(LV_OBJ(I).COL1||' '||LV_OBJ(I).COL2||' '||LV_OBJ(I).COL3);
11 END LOOP;
12 END;
13 /
test
1 1AV 18-NOV-15 02.04.29.000000 AM
2 2AV 19-NOV-15 02.04.29.000000 AM
3 3AV 20-NOV-15 02.04.29.000000 AM
4 4AV 21-NOV-15 02.04.29.000000 AM
5 5AV 22-NOV-15 02.04.29.000000 AM
6 6AV 23-NOV-15 02.04.29.000000 AM
7 7AV 24-NOV-15 02.04.29.000000 AM
8 8AV 25-NOV-15 02.04.29.000000 AM
9 9AV 26-NOV-15 02.04.29.000000 AM
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.12
SQL> spool off;

Update with after insert trigger on same table

I have a to write a insert trigger on a tableA. which will perform update with same table but different column. I am getting error while doing this. My trigger is
create or replace trigger trigger_A
after insert on table_A
begin
update table_A set col1=1 where col1 is null;
end;
I have an application will perform col2 alone will be inserted and col1 will be kept null. so my trigger will give value for col1 once the row is inserted. But i am getting error saying "Trigger is failed and invalid" when a row is inserted.
How to do this. TIA.
If you want to assign a simple default value, the easiest way is to declare it on the table, using the DEFAULT clause.
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date)
4 /
Table created.
SQL> insert into t42 (col2) values (sysdate)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2
---------- ---------
1 03-AUG-11
SQL>
This works with literals or pseudocolumns such as SYSDATE or USER. If you want to derive a more complicated value with a user-defined function or a sequence, you will need to use
a trigger.
Here is a new version of the table...
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date default sysdate
4 , col3 varchar2(30) default user
5 , col4 number )
6 /
Table created.
SQL>
... with a trigger:
SQL> create or replace trigger t42_trg
2 before insert or update
3 on t42
4 for each row
5 begin
6 if :new.col4 is null
7 then
8 :new.col4 := my_seq.nextval;
9 end if;
10 end;
11 /
Trigger created.
SQL> insert into t42 (col1, col2, col3)
2 values (99, sysdate, 'MR KNOX')
3 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
SQL>
Note that although every column on the table is defaultable, I have to populate at least one column to make the SQL valid:
SQL> insert into t42 values ()
2 /
insert into t42 values ()
*
ERROR at line 1:
ORA-00936: missing expression
SQL>
But I can pass in NULL to COL4 to get a completely defaulted record:
SQL> insert into t42 (col4) values (null)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
1 03-AUG-11 APC 162
SQL>
Caveat lector: my trigger uses the new 11g syntax. In previous versions we have to assign the sequence value using a SELECT statement:
select my_seq.nextval
into :new.col4
from dual;
You cannot update a table where the trigger is invoked:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
Doing so will generate Error 1442:
Error Code: 1442
Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
In short, we are not allowed to update the table in use - but your case is simple, only want to update the field if it's NULL, for this choose BEFORE INSERT ON trigger, this way you can update all the fields of the new/current entry/row (as it has not been entered yet):
DELIMITER //
DROP TRIGGER IF EXISTS trigger_A//
CREATE TRIGGER trigger_A BEFORE INSERT ON table_A
FOR EACH ROW BEGIN
IF NEW.col1 IS NULL THEN
set NEW.col1 = <some-value>;
ENF IF;
END;
//
DELIMITER ;

Query to output values which are not present in table

Need help in Oracle query
Requirement:
I have 5 rows in a table lets say ID = 1, 2, 3, 4, 5
Requirement is as such that user may pass any value as input and if that value is not there in table then query should return me the value which is not present.
Ex:
1. If user passes 9 then Oracle query should return the output as 9
2. If user passes 1,2,10 then Oracle query should return the output as 10
as 9 and 10 in above example are not in table.
I am using following query but not getting result.
SELECT ID
FROM TABLE_NAME WHERE ID NOT IN
(SELECT ID
FROM TABLE_NAME where ID NOT in (1,2,10))
create table z (id number);
Table created.
SQL> insert into z values (1);
1 row created.
SQL> insert into z values (2);
1 row created.
SQL> insert into z values (3);
1 row created.
SQL> insert into z values (4);
1 row created.
SQL> insert into z values (5);
1 row created.
SQL> select 10 id from dual
2 minus
3 select id from z;
ID
----------
10
You could use a nested table as input:
SQL> CREATE TABLE table_name (ID NUMBER NOT NULL);
Table created
SQL> INSERT INTO table_name (SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 5);
5 rows inserted
SQL> CREATE TYPE tab_number AS TABLE OF NUMBER;
2 /
Type created
SQL> SELECT *
2 FROM TABLE(tab_number(1,2,10)) x
3 WHERE x.column_value NOT IN (SELECT ID FROM table_name);
COLUMN_VALUE
------------
10