I was wondering what would be the preferred technique in Oracle to copy multiple records into a database that ignored duplicate values on a certain index. the statements are stated explicitly in the statement and don't come from another table
INSERT INTO EXAMPLE (A, B, C, D) VALUES (null,'example1','example2',EXAMPLE_SEQ.nextval);
INSERT INTO EXAMPLE (A, B, C, D) VALUES (null,'example2','example3',EXAMPLE_SEQ.nextval);
INSERT INTO EXAMPLE (A, B, C, D) VALUES (null,'example4','example5',EXAMPLE_SEQ.nextval);
I am currently doing it like this and checking manually, but need to find a way so that these can be handled as scripts
If you've decided to stick with INSERTs you can prevent insertion of duplicate rows by using constraints whether it primary key or unique key. If it happens to violate a unique constraint your script will stop and you'll have to roollback all changes made by previous inserts(unless you have committed every single of them). To handle that exception you could write a similar pls/sql block.
declare
l_unique_exception exception;
pragma exception_init(l_unique_exception, -1);
begin
insert into test(id, test_vector)
values(1, 123);
insert into test(id, test_vector)
values(1, 123);
......
Insert into
commit;
exception
when l_unique_exception
then process the exception;
end;
IN ADDITION
If you want to proceed after one of the inserts raises an exception then the following example might be in handy.
Create a table that going to contain errors. For example.
CREATE TABLE tb_errors (
ErrorTag varchar2(123)
)
Provide an error logging invoking CREATE_ERROR_LOG procedure of DBMS_ERRLOG package
DBMS_ERRLOG.CREATE_ERROR_LOG('YourDmlTable. Test in this case', 'tb_errors');
Add log errors into clause to each insert
Here is an example
declare
begin
insert into test(id, col1)
values(1, 123)
log errors into tb_errors('simple expression') reject limit unlimited;
insert into test(id, col1)
values(1, 123)
log errors into tb_errors('simple expression') reject limit unlimited;
insert into test(id, col1)
values(1, 123)
log errors into tb_errors('simple expression') reject limit unlimited;
commit;
end;
After your script is completed you can query error logging table, tb_errors in this case, to see what went wrong.
You should look at the MERGE syntax.
http://en.wikipedia.org/wiki/Merge_(SQL)
merge example target
using (select 1 as id, 'a' as val) as source
on source.id = target.id
and source.val = target.val
when not matched then
insert (id, val) values (source.id, source.val);
I suggest you to use LOG error clause if you have a goal to provide additional treatment of incorrect data. Please consider http://www.oracle-base.com/articles/10g/dml-error-logging-10gr2.php - good example is there.
Related
I have a table which have columns like id, name, start_date, end_date
and 2 unique constraints
unique(id, name, start_date)
and
unique(id, name, end_date)
now when I am writing insert query for this table, I have something like
insert into table (id, name, start_date, end_date)
values (1, 'test', 'example-start-date', 'example-end-date')
on conflict (id, name, start_date) set something
on conflict (id, name, end_date) set something
but getting errors, is this not allowed?
thanks
The answer depends on something.
For DO NOTHING, the answer is simply to use a single clause without specifying the columns:
ON CONFLICT DO NOTHING
That can deal with conflicts on multiple unique constraints.
For DO UPDATE, there is no straightforward solution. The syntax diagram in the documentation shows that you can only have a single ON CONFLICT clause that determines a single unique index.
You could use procedural code to do it in the old-fashioned way, for example in PL/pgSQL:
DECLARE
v_constraint text;
BEGIN
LOOP -- endless loop until INSERT or UPDATE succeeds
BEGIN
INSERT INTO ...;
EXIT; -- leave loop if INSERT succeeds
EXCEPTION
WHEN unique_violation THEN
GET STACKED DIAGNOSTICS v_constraint := CONSTRAINT_NAME;
END;
CASE v_constraint
WHEN 'constraint_name_1' THEN
UPDATE ...;
WHEN 'constraint_name_2' THEN
UPDATE ...;
END CASE;
EXIT WHEN FOUND; -- leave loop if UPDATE changed a row
END LOOP;
END;
SET TERM ^ ;
CREATE TRIGGER ADDPWDHASHHISTORY FOR USERS ACTIVE
AFTER INSERT OR UPDATE POSITION 3
AS
BEGIN
INSERT INTO USERSPWDHASHHISTORY
(id,
USERID,
FIO,
PWDHASH,
ATTIME)
values (gen_id(GEN_PWDHASHHIS_ID,1) , new.id, new.fio, new.pwdhash, CURRENT_TIMESTAMP);
END^
SET TERM ; ^
This trigger is triggered to change any field in the USERS table. I need to change it, to trigger only on changing the PWDNASH field. Please help
A trigger on insert or update will fire for any insert or update on the table. If you want to have conditional behaviour on specific fields, you will need to do that in the trigger body.
In this case, you will need to do something like:
SET TERM ^ ;
CREATE TRIGGER ADDPWDHASHHISTORY FOR USERS ACTIVE
AFTER INSERT OR UPDATE POSITION 3
AS
BEGIN
if (inserting or new.PWDHASH is distinct from old.PWDHASH) then
INSERT INTO USERSPWDHASHHISTORY
(id,
USERID,
FIO,
PWDHASH,
ATTIME)
values (gen_id(GEN_PWDHASHHIS_ID,1) , new.id, new.fio, new.pwdhash, CURRENT_TIMESTAMP);
END^
SET TERM ; ^
That is, the inserting condition will be true when the trigger was fired for an insert, while the new.PWDHASH is distinct from old.PWDHASH checks if the value has changed in an update. You could also use the condition inserting or updating and new.PWDHASH is distinct from old.PWDHASH, but that is not really necessary, so I opted for the shorter version.
See also:
INSERTING
IS [NOT] DISTINCT FROM
OLD and NEW Context Variables
i have to insert 3 rows in a table as per my conditions.
let's assume 3 insert statement are insert A, insert B, insert C.
if INSERT A success then automatically INSERT C also should be executed and as well as INSERT C success then automatically insert A should be executed.
if both A & C failed then only insert B should be executed.
This is what I meant (while discussing it with #XING); don't mind WHEN OTHERS, it just shows that something will need to be handled.
begin
insert into A values (1); --> if this succeeds,
insert into B values (1); --> insert into B will automatically follow
exception
when others then --> if anything goes wrong above
insert into C values (1); --> insert into C will be done.
end; --> But, what if it fails? Could that happen?
--> If so, it'll have to be handled as well.
Is there any way to write an SQL input file for sqlite that would somehow "throw" an error, eg. exited the transaction with rollback, if a condition isn't met?
I have a script that is supposed to do something, but only if there is a certain row in one table. If it's not there, the execution of the script might have fatal results and corrupt the db.
The script is only started on demand right now, but I would prefer to add a fail-safe which would prevent its execution in case there is some issue.
Basically what I need is something like
/* IF */ SELECT value FROM meta WHERE key = 'version' /* != hardcoded_version_string THROW SOME EXCEPTION */
Is there any way to accomplish that? In Postgre / Oracle this could be done using PLSQL but I am not sure if sqlite support any such a thing?
Triggers can use the RAISE function to generate errors:
CREATE VIEW test AS SELECT NULL AS value;
CREATE TRIGGER test_insert
INSTEAD OF INSERT ON test
BEGIN
SELECT RAISE(FAIL, 'wrong value')
WHERE NEW.value != 'fixed_value';
END;
INSERT INTO test SELECT 'fixed_value';
INSERT INTO test SELECT 'whatever';
Error: wrong value
Is there any way to write an SQL input file for sqlite that would
somehow "throw" an error, eg. exited the transaction with rollback, if
a condition isn't met?
One workaround may be to create dummy table and explicitly violate NULL constraint:
CREATE TABLE meta("key" VARCHAR(100));
INSERT INTO meta("key") VALUES ('version');
CREATE TEMPORARY TABLE dummy(col INT NOT NULL);
Transaction:
BEGIN TRANSACTION;
INSERT INTO dummy(col)
SELECT NULL -- explicit insert of NULL
FROM meta
WHERE "key" = 'version';
-- Error: NOT NULL constraint failed: dummy.col
-- rest code
INSERT INTO meta("key")
VALUES ('val1');
INSERT INTO meta("key")
VALUES ('val2');
-- ...
COMMIT;
SqlFiddleDemo
Keep in mind that SQLite is not procedural language and this solution is a bit ugly.
I have to insert some data in oracle DB, without previously checking if it already exist.
Does exist any way, transiction on oracle to catch the exception inside the query and handle it to don't return any exception?
It would be perfect something in mysql's style like
insert .... on duplicate key a=a
You can use MERGE. The syntax is a bit different from a regular insert though;
MERGE INTO test USING (
SELECT 1 AS id, 'Test#1' AS value FROM DUAL -- your row to insert here
) t ON (test.id = t.id) -- duplicate check
WHEN NOT MATCHED THEN
INSERT (id, value) VALUES (t.id, t.value); -- insert if no duplicate
An SQLfiddle to test with.
If you can use PL/SQL, and you have a unique index on the columns where you don't want any duplicates, then you can catch the exception and ignore it:
begin
insert into your_table (your_col) values (your_value);
exception
when dup_val_on_index then null;
end;
Since 11g there is the ignore_row_on_dupkey_index hint that ignores Unique Constraint-exceptions and let the script go on with the next row if there is any, see Link. The exception is not logged. It needs two arguments, the table name and the index name.
INSERT /*+ ignore_row_on_dupkey_index(my_table, my_table_idx) */
INTO my_table(id,name,phone)
VALUES (24,'Joe','+49 19450704');