Oracle 18c multiple insert errors - oracle

I need to complete multiple insert in Oracle table about 100k rows. But when I try do like this:
insert into table_name (ID, code, date_t)
values (schema_name.SEQ$table_name.NEXTVAL, '232323232323' , to_date('2020-09-01','YYYY-MM-DD'));
insert into table_name (ID, code, date_t)
values (schema_name.SEQ$table_name.NEXTVAL, '242424242424' , to_date('2020-09-01','YYYY-MM-DD'));
I'm getting an error :
ora-00933 sql command not properly ended
I tried to make it using insert all:
insert ALL
INTO table_name (ID, code, date_t) values (schema_name.SEQ$table_name.NEXTVAL, '232323232323' , to_date('2020-09-01','YYYY-MM-DD'))
INTO table_name (ID, code, date_t) values (schema_name.SEQ$table_name.NEXTVAL, '242424242424' , to_date('2020-09-01','YYYY-MM-DD'))
SELECT 1 FROM schema_name.table_name;
But I'm getting an error:
ORA-00001: unique constraint (constraint_name) violated
How can I solve that errors or How can I make multiple insert in oracle?

There's nothing wrong with those inserts.
SQL> desc table_name
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
CODE VARCHAR2(20)
DATE_T DATE
SQL> INSERT INTO table_name (ID, code, date_t)
2 VALUES (scott.SEQ$table_name.NEXTVAL,
3 '232323232323',
4 TO_DATE ('2020-09-01', 'YYYY-MM-DD'));
1 row created.
SQL> INSERT INTO table_name (ID, code, date_t)
2 VALUES (scott.SEQ$table_name.NEXTVAL,
3 '242424242424',
4 TO_DATE ('2020-09-01', 'YYYY-MM-DD'));
1 row created.
SQL>
Therefore, it must be the way you're running those inserts. Let me guess: if it is TOAD and there's no empty line between each insert, it'll return ORA-00933 so "solution" is to execute them as a script (F5).
Some other GUI might require different action.
So - how exactly are you running those commands?

Related

Creating List partition to an already Existing Table

I am trying to Create a list partition Based on the column "REFRESH_FLAG_Y" which has only Y and N as its Values, Below is the Alter Table used to Create the partition
ALTER TABLE "EDW"."LABOR_SCHEDULE_DAY_F" MODIFY
PARTITION BY LIST ("REFRESH_FLAG")
(PARTITION "REFRESH_FLAG_Y" VALUES ('Y') ,
PARTITION "REFRESH_FLAG_N" VALUES ('N')) ;
COMMIT;
But Whenever I execute the code I get an Error message
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
You did tag the question with Oracle 11g tag; do you really use it?
This is a 12c example; it works if everything is OK:
SQL> create table labor_schedule_day_f as
2 select 1 id, 'Y' refresh_flag from dual union all
3 select 2 id, 'N' refresh_flag from dual;
Table created.
SQL> alter table labor_schedule_Day_f modify
2 partition by list (refresh_flag)
3 (partition refresh_flag_y values ('Y'),
4 partition refresh_flag_n values ('N')
5 );
Table altered.
Error you reported means this:
SQL> drop table labor_schedule_day_f;
Table dropped.
SQL> create table labor_schedule_day_f as
2 select 1 id, 'Y' refresh_flag from dual union all
3 select 2 id, 'N' refresh_flag from dual;
Table created.
Insert a row whose REFRESH_FLAG isn't Y nor N (so it violates the rule you specified):
SQL> insert into labor_schedule_day_f values (3, 'X');
1 row created.
Using the same ALTER TABLE statement as previously:
SQL> alter table labor_schedule_Day_f modify
2 partition by list (refresh_flag)
3 (partition refresh_flag_y values ('Y'),
4 partition refresh_flag_n values ('N')
5 );
alter table labor_schedule_Day_f modify
*
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
SQL>
See? Error you got, which means that
which has only Y and N as its Values
isn't true.
P.S. You'd get the same result even if refresh_flag was NULL for some rows.

Difference between execute current query and execute current script in oracle

I need to complete multiple inserts in Oracle.
insert into table_name (ID, code, date_t)
values (schema_name.SEQ$table_name.NEXTVAL, '232323232323' , to_date('2020-09-01','YYYY-MM-DD'));
insert into table_name (ID, code, date_t)
values (schema_name.SEQ$table_name.NEXTVAL, '242424242424' , to_date('2020-09-01','YYYY-MM-DD'));
I can't complete them when trying to use simple execute because it gives me an error: ora-00933 sql command not properly ended
But when I using execute as script it works fine.
The main problem is that I can't explain that to people how to complete that script in the console.
Maybe there are some other options to complete multiple inserts in Oracle?
Use INSERT ... SELECT:
insert into table_name (ID, code, date_t)
WITH data (code, date_t) AS (
SELECT '232323232323', DATE '2020-09-01' FROM DUAL UNION ALL
SELECT '242424242424', DATE '2020-09-01' FROM DUAL
)
SELECT SEQ$table_name.NEXTVAL, code, date_t FROM data;
You could also use INSERT ALL; however, you have to jump through some hoops to get it to work with a sequence so I would suggest not using this option:
INSERT ALL
WHEN rn = 1 THEN
INTO table_name (id, code, date_t)
VALUES (SEQ$table_name.NEXTVAL, '252525252525', DATE '2020-09-01')
WHEN rn = 2 THEN
INTO table_name (id, code, date_t)
VALUES (SEQ$table_name.CURRVAL, '262626262626', DATE '2020-09-01')
SELECT LEVEL AS rn
FROM DUAL
CONNECT BY LEVEL <= 2;

Validate ALL the constraints when inserting a record in Oracle [duplicate]

I have a table in Oracle with several constraints. When I insert a new record and not all constraints are valid, then Oracle raise only the "first" error. How to get all violations of my record?
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL,
COL_2 NUMBER NOT NULL,
COL_3 NUMBER NOT NULL,
COL_4 NUMBER NOT NULL
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
ORA-01400: cannot insert NULL into ("USER_4_8483C"."A_TABLE_TEST"."COL_2")
I would like to get something like this:
Column COL_2: cannot insert NULL
Column COL_3: cannot insert NULL
This would be also sufficient:
Column COL_2: not valid
Column COL_3: not valid
Of course I could write a trigger and check each column individually, but I like to prefer constraints rather than triggers, they are easier to maintain and don't require manually written code.
Any idea?
There no straightforward way to report all possible constraint violations. Because when Oracle stumble on first violation of a constraint, no further evaluation is possible, statement fails, unless that constraint is deferred one or the log errors clause has been included in the DML statement. But it should be noted that log errors clause won't be able to catch all possible constraint violations, just records first one.
As one of the possible ways is to:
create exceptions table. It can be done by executing ora_home/rdbms/admin/utlexpt.sql script. The table's structure is pretty simple;
disable all table constraints;
execute DMLs;
enable all constraints with exceptions into <<exception table name>> clause. If you executed utlexpt.sql script, the name of the table exceptions are going to be stored would be exceptions.
Test table:
create table t1(
col1 number not null,
col2 number not null,
col3 number not null,
col4 number not null
);
Try to execute an insert statement:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
Error report -
SQL Error: ORA-01400: cannot insert NULL into ("HR"."T1"."COL2")
Disable all table's constraints:
alter table T1 disable constraint SYS_C009951;
alter table T1 disable constraint SYS_C009950;
alter table T1 disable constraint SYS_C009953;
alter table T1 disable constraint SYS_C009952;
Try to execute the previously failed insert statement again:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
1 rows inserted.
commit;
Now, enable table's constraints and store exceptions, if there are any, in the exceptions table:
alter table T1 enable constraint SYS_C009951 exceptions into exceptions;
alter table T1 enable constraint SYS_C009950 exceptions into exceptions;
alter table T1 enable constraint SYS_C009953 exceptions into exceptions;
alter table T1 enable constraint SYS_C009952 exceptions into exceptions;
Check the exceptions table:
column row_id format a30;
column owner format a7;
column table_name format a10;
column constraint format a12;
select *
from exceptions
ROW_ID OWNER TABLE_NAME CONSTRAINT
------------------------------ ------- ------- ------------
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009951
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009953
Two constraints have been violated. To find out column names, simply refer to user_cons_columns data dictionary view:
column table_name format a10;
column column_name format a7;
column row_id format a20;
select e.table_name
, t.COLUMN_NAME
, e.ROW_ID
from user_cons_columns t
join exceptions e
on (e.constraint = t.constraint_name)
TABLE_NAME COLUMN_NAME ROW_ID
---------- ---------- --------------------
T1 COL2 AAAWmUAAJAAAF6WAAA
T1 COL4 AAAWmUAAJAAAF6WAAA
The above query gives us column names, and rowids of problematic records. Having rowids at hand, there should be no problem to find those records that cause constraint violation, fix them, and re-enable constraints once again.
Here is the script that has been used to generate alter table statements for enabling and disabling constraints:
column cons_disable format a50
column cons_enable format a72
select 'alter table ' || t.table_name || ' disable constraint '||
t.constraint_name || ';' as cons_disable
, 'alter table ' || t.table_name || ' enable constraint '||
t.constraint_name || ' exceptions into exceptions;' as cons_enable
from user_constraints t
where t.table_name = 'T1'
order by t.constraint_type
You would have to implement a before-insert trigger to loop through all the conditions that you care about.
Think about the situation from the database's perspective. When you do an insert, the database can basically do two things: complete the insert successfully or fail for some reason (typically a constraint violation).
The database wants to proceed as quickly as possibly and not do unnecessary work. Once it has found the first complaint violation, it knows that the record is not going into the database. So, the engine wisely returns an error and stops checking further constraints. There is no reason for the engine to get the full list of violations.
In the meantime I found a lean solution using deferred constraints:
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_2 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_3 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_4 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
DECLARE
CHECK_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(CHECK_CONSTRAINT_VIOLATED, -2290);
REF_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(REF_CONSTRAINT_VIOLATED , -2292);
CURSOR CheckConstraints IS
SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME
FROM USER_CONSTRAINTS
JOIN USER_CONS_COLUMNS USING (TABLE_NAME, CONSTRAINT_NAME)
WHERE TABLE_NAME = 'A_TABLE_TEST'
AND DEFERRED = 'DEFERRED'
AND STATUS = 'ENABLED';
BEGIN
FOR aCon IN CheckConstraints LOOP
BEGIN
EXECUTE IMMEDIATE 'SET CONSTRAINT '||aCon.CONSTRAINT_NAME||' IMMEDIATE';
EXCEPTION
WHEN CHECK_CONSTRAINT_VIOLATED OR REF_CONSTRAINT_VIOLATED THEN
DBMS_OUTPUT.PUT_LINE('Constraint '||aCon.CONSTRAINT_NAME||' at Column '||aCon.COLUMN_NAME||' violated');
END;
END LOOP;
END;
It works with any check constraint (not only NOT NULL). Checking FOREIGN KEY Constraint should work as well.
Add/Modify/Delete of constraints does not require any further maintenance.

Oracle: how to drop a subpartition of a specific partition

I am using an oracle 11 table with interval partitioning and list subpartitioning like this (simplified):
CREATE TABLE LOG
(
ID NUMBER(15, 0) NOT NULL PRIMARY KEY
, MSG_TIME DATE NOT NULL
, MSG_NR VARCHAR2(16 BYTE)
) PARTITION BY RANGE (MSG_TIME) INTERVAL (NUMTOYMINTERVAL (1,'MONTH'))
SUBPARTITION BY LIST (MSG_NR)
SUBPARTITION TEMPLATE (
SUBPARTITION login VALUES ('FOO')
, SUBPARTITION others VALUES (DEFAULT)
)
(PARTITION oldvalues VALUES LESS THAN (TO_DATE('01-01-2010','DD-MM-YYYY')));
How do I drop a specific subpartitition for a specific month without knowing the (system generated) name of the subpartition? There is a syntax "alter table ... drop subpartition for (subpartition_key_value , ...)" but I don't see a way to specify the month for which I am deleting the subpartition. The partition administration guide does not give any examples, either. 8-}
You can use the metadata tables to get the specific subpartition name:
SQL> insert into log values (1, sysdate, 'FOO');
1 row(s) inserted.
SQL> SELECT p.partition_name, s.subpartition_name, p.high_value, s.high_value
2 FROM user_tab_partitions p
3 JOIN
4 user_tab_subpartitions s
5 ON s.table_name = p.table_name
6 AND s.partition_name = p.partition_name
7 AND p.table_name = 'LOG';
PARTITION_NAME SUBPARTITION_NAME HIGH_VALUE HIGH_VALUE
--------------- ------------------ ------------ ----------
OLDVALUES OLDVALUES_OTHERS 2010-01-01 DEFAULT
OLDVALUES OLDVALUES_LOGIN 2010-01-01 'FOO'
SYS_P469754 SYS_SUBP469753 2012-10-01 DEFAULT
SYS_P469754 SYS_SUBP469752 2012-10-01 'FOO'
SQL> alter table log drop subpartition SYS_SUBP469752;
Table altered.
If you want to drop a partition dynamically, it can be tricky to find it with the ALL_TAB_SUBPARTITIONS view because the HIGH_VALUE column may not be simple to query. In that case you could use DBMS_ROWID to find the subpartition object_id of a given row:
SQL> insert into log values (4, sysdate, 'FOO');
1 row(s) inserted.
SQL> DECLARE
2 l_rowid_in ROWID;
3 l_rowid_type NUMBER;
4 l_object_number NUMBER;
5 l_relative_fno NUMBER;
6 l_block_number NUMBER;
7 l_row_number NUMBER;
8 BEGIN
9 SELECT rowid INTO l_rowid_in FROM log WHERE id = 4;
10 dbms_rowid.rowid_info(rowid_in =>l_rowid_in ,
11 rowid_type =>l_rowid_type ,
12 object_number =>l_object_number,
13 relative_fno =>l_relative_fno ,
14 block_number =>l_block_number ,
15 row_number =>l_row_number );
16 dbms_output.put_line('object_number ='||l_object_number);
17 END;
18 /
object_number =15838049
SQL> select object_name, subobject_name, object_type
2 from all_objects where object_id = '15838049';
OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE
--------------- --------------- ------------------
LOG SYS_SUBP469757 TABLE SUBPARTITION
As it turns out, the "subpartition for" syntax does indeed work, though that seems to be a secret Oracle does not want to tell you about. :-)
ALTER TABLE TB_LOG_MESSAGE DROP SUBPARTITION FOR
(TO_DATE('01.02.2010','DD.MM.YYYY'), 'FOO')
This deletes the subpartition that would contain MSG_TIME 2010/02/01 and MSG_NR FOO. (It is not necessary that there is an actual row with this exact MSG_TIME and MSG_NR. It throws an error if there is no such subpartition, though.)
Thanks for the post - it was very useful for me.
One observation though on the above script to identify the partition and delete it:
The object_id returned by dbms_rowid.rowid_info is not the object_id of the all_objects table. It is actually the data_object_id. It is observed that usually these ids match. However, after truncating the partitioned table several times, these ids diverged in my database. Hence it might be reasonable to instead use the data_object_id to find out the name of the partition:
select object_name, subobject_name, object_type
from all_objects where data_object_id = '15838049';
From the table description of ALL_OBJECTS:
OBJECT_ID Object number of the object
DATA_OBJECT_ID Object number of the segment which contains the object
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_rowid.htm
In the sample code provided in the above link, DBMS_ROWID.ROWID_OBJECT(row_id) is used instead to derive the same information that is given by dbms_rowid.rowid_info. However, the documentation around this sample mentions that it is a data object number from the ROWID.
Examples
This example returns the ROWID for a row in the EMP table, extracts
the data object number from the ROWID, using the ROWID_OBJECT function
in the DBMS_ROWID package, then displays the object number:
DECLARE object_no INTEGER; row_id ROWID; ... BEGIN
SELECT ROWID INTO row_id FROM emp
WHERE empno = 7499; object_no := DBMS_ROWID.ROWID_OBJECT(row_id); DBMS_OUTPUT.PUT_LINE('The obj. # is
'|| object_no); ...

Oracle Equivalent to MySQL INSERT IGNORE?

I need to update a query so that it checks that a duplicate entry does not exist before insertion. In MySQL I can just use INSERT IGNORE so that if a duplicate record is found it just skips the insert, but I can't seem to find an equivalent option for Oracle. Any suggestions?
If you're on 11g you can use the hint IGNORE_ROW_ON_DUPKEY_INDEX:
SQL> create table my_table(a number, constraint my_table_pk primary key (a));
Table created.
SQL> insert /*+ ignore_row_on_dupkey_index(my_table, my_table_pk) */
2 into my_table
3 select 1 from dual
4 union all
5 select 1 from dual;
1 row created.
Check out the MERGE statement. This should do what you want - it's the WHEN NOT MATCHED clause that will do this.
Do to Oracle's lack of support for a true VALUES() clause the syntax for a single record with fixed values is pretty clumsy though:
MERGE INTO your_table yt
USING (
SELECT 42 as the_pk_value,
'some_value' as some_column
FROM dual
) t on (yt.pk = t.the_pke_value)
WHEN NOT MATCHED THEN
INSERT (pk, the_column)
VALUES (t.the_pk_value, t.some_column);
A different approach (if you are e.g. doing bulk loading from a different table) is to use the "Error logging" facility of Oracle. The statement would look like this:
INSERT INTO your_table (col1, col2, col3)
SELECT c1, c2, c3
FROM staging_table
LOG ERRORS INTO errlog ('some comment') REJECT LIMIT UNLIMITED;
Afterwards all rows that would have thrown an error are available in the table errlog. You need to create that errlog table (or whatever name you choose) manually before running the insert using DBMS_ERRLOG.CREATE_ERROR_LOG.
See the manual for details
I don't think there is but to save time you can attempt the insert and ignore the inevitable error:
begin
insert into table_a( col1, col2, col3 )
values ( 1, 2, 3 );
exception when dup_val_on_index then
null;
end;
/
This will only ignore exceptions raised specifically by duplicate primary key or unique key constraints; everything else will be raised as normal.
If you don't want to do this then you have to select from the table first, which isn't really that efficient.
Another variant
Insert into my_table (student_id, group_id)
select distinct p.studentid, g.groupid
from person p, group g
where NOT EXISTS (select 1
from my_table a
where a.student_id = p.studentid
and a.group_id = g.groupid)
or you could do
Insert into my_table (student_id, group_id)
select distinct p.studentid, g.groupid
from person p, group g
MINUS
select student_id, group_id
from my_table
A simple solution
insert into t1
select from t2
where not exists
(select 1 from t1 where t1.id= t2.id)
This one isn't mine, but came in really handy when using sqlloader:
create a view that points to your table:
CREATE OR REPLACE VIEW test_view
AS SELECT * FROM test_tab
create the trigger:
CREATE OR REPLACE TRIGGER test_trig
INSTEAD OF INSERT ON test_view
FOR EACH ROW
BEGIN
INSERT INTO test_tab VALUES
(:NEW.id, :NEW.name);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN NULL;
END test_trig;
and in the ctl file, insert into the view instead:
OPTIONS(ERRORS=0)
LOAD DATA
INFILE 'file_with_duplicates.csv'
INTO TABLE test_view
FIELDS TERMINATED BY ','
(id, field1)
How about simply adding an index with whatever fields you need to check for dupes on and say it must be unique? Saves a read check.
yet another "where not exists"-variant using dual...
insert into t1(id, unique_name)
select t1_seq.nextval, 'Franz-Xaver' from dual
where not exists (select 1 from t1 where unique_name = 'Franz-Xaver');

Resources