Capture values that trigger DUP_VAL_ON_INDEX - oracle

Given this example (DUP_VAL_ON_INDEX Exception), is it possible to capture the values that violated the constraint so they may be logged?
Would the approach be the same if there are multiple violations generated by a bulk insert?
BEGIN
-- want to capture '01' and '02'
INSERT INTO Employee(ID)
SELECT ID
FROM (
SELECT '01' ID FROM DUAL
UNION
SELECT '02' ID FROM DUAL
);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
-- log values here
DBMS_OUTPUT.PUT_LINE('Duplicate value on an index');
END;

Ideally, I would suggest using DML error logging. For example
Create the error log table
begin
dbms_errlog.create_error_log( dml_table_name => 'EMPLOYEE',
err_log_table_name => 'EMPLOYEE_ERR' );
end;
Use DML error logging
BEGIN
insert into employee( id )
select id
from (select '01' id from dual
union all
select '02' from dual)
log errors into employee_err
reject limit unlimited;
END;
For every row that fails, this will log the data for the row into the EMPLOYEE_ERR table along with the exception. You can then query the error log table to see all the errors rather than getting just the first row that failed.
If creating the error log table isn't an option, you could move from SQL to PL/SQL with bulk operations. That will be slower but you could use the SAVE EXCEPTIONS clause of the FORALL statement to create a nested table of exceptions that you could then iterate over.

For people who would be interested to know more about this, please go through this link.

Related

FRM-40735: post-insert trigger raised unhandled exception ora-01722

I create POST-INSERT Trigger on Block when I am getting this error on Oracle Forms 11gR2
"FRM-40735: post-insert trigger raised unhandled exception ora-01722"
POST-INSERT Trigger Code:
Insert into we_group (GROUP_ID, GROUP_SIZE, NRSP_STATUS, GROUP_RECEIVED)
Select DISTINCT GROUP_ID, ('Select COUNT(*) from we_group_hof_k'),
nrsp_status, sysdate
from we_group_hof_k;
commit_form;
How to solve this problem?
Remove ' to prevent number to char convertion
Select DISTINCT GROUP_ID, (select COUNT(*) from we_group_hof_k),
nrsp_status, sysdate
from we_group_hof_k;
Consider using analytic version of the COUNT function (if Forms version you use supports it; 10g doesn't, I can't tell about 11g):
INSERT INTO we_group (GROUP_ID,
group_size,
nrsp_status,
group_received)
SELECT DISTINCT GROUP_ID,
COUNT (*) OVER (ORDER BY NULL),
nrsp_status,
SYSDATE
FROM we_group_hof_k;
It evidently seems ORA-01722 raises due to an attempt to insert a quoted string
( 'Select COUNT(*) from we_group_hof_k' ) into a numeric column( GROUP_SIZE ).
So, first of all you need to get rid of those quotes, and even whole sub-select, since you already try to select from the same table in the main query, and just consider to include a group by clause instead:
Insert Into we_group(group_id, group_size, nrsp_status, group_received)
Select group_id,Count(1),nrsp_status, sysdate
From we_group_hof_k
Group By group_id,nrsp_status;
Lastly do not use a commit or commit_form inside a POST-INSERT trigger, that usage is considered as illegal restricted there.

PL/SQL ORA-01400: cannot insert NULL into when using CASE

I have the following PL/SQL Block:
declare
vDayType varchar2(10);
TYPE Holidays is table of varchar2(5);
hd Holidays := Holidays('01.01','15.01','19.01','28.05','04.07','08.10','11.11','22.11','25.12');
begin
for s in (select distinct saleDate from Sales) loop
vDayType := case when TO_CHAR(s.saleDate, 'dd.mm') member of hd then
'Holiday'
when to_char(s.saleDate, 'd') IN (1,7) then
'Weekend'
else
'Weekday'
end;
insert into times (saleDay, dayType) values (s.saleDate, vDayType);
end loop;
end;
/
This pulls data from a OLTP Table named SALES and inserts it into the dimension table named TIMES. It incorporates a CASE statement to "calculate" days that are Holidays, Weekdays, or Weekends. Unfortunately, when I run this code, I'm getting the following error:
ERROR at line 1:
ORA-01400: cannot insert NULL into ("CM420A01"."TIMES"."SALEDAY")
ORA-06512: at line 14
I believe it is inserting NULL because I have it set to only SELECT DISTINCT values from saleDate in the SALES OLTP Table. I'm assuming it's still trying to insert the dayType from the CASE statement, even when it's not inserting a saleDay because of the DISTINCT statement, which is thus inserting NULL into the saleDay column and causing the error.
Any tips/tricks to recover from this issue so it'll run without error?
Added WHERE SALES.vehicleStatus = 'sold' clause due to NULL values that were being inserted for vehicles what were listed as pending or available and didn't yet have saleDate's......

concurrency in oracle plsql

I have a PL/SQL package in Oracle that its important function is :
function checkDuplicate(in_id in varchar2) return boolean is
cnt number;
begin
select count(*)
into cnt
from tbl_Log t
where t.id = in_id
if (cnt > 0) then
// It means the request is duplicate on in_id
return false;
end if;
insert into tbl_log (id,date) values(in_id , sysdate);
return true;
end;
When two requests call this function concurrently, both of them passed this function and two the same in_id inserted in tbl_log.
Note: tbl_log doesn't have a PK for performance issues.
Are there any solutions?
" both of them passed this function and two the same in_id inserted in tbl_log"
Oracle operates at the READ COMMITTED isolation level, so the select can only find committed records. If one thread has inserted a record for a given value but hasn't committed the transaction another thread looking for the same value will come up empty.
"Note: tbl_log doesn't have a PK for performance issues. "
The lessons of history are clear: tables without integrity constraints inevitably fall into data corruption.
"I want to recognize the duplication with this function ... Are there any solutions?"
You mean apart from adding a primary key constraint? There is no more efficient way of trapping duplication than a primary key. Maybe you should look at the performance issues. Plenty of applications mange to handle millions of inserts and still enforce integrity constraints. You should also look at the Java layer: why have you got multiple threads submitting the same ID?
Note: tbl_log doesn't have a PK for performance issues.
There is no PK nor unique index on this column in order to "avoid performance issues", but there are hundreds or thousands queries like SELECT ... WHERE t.id = .. running against this table. These queries must use a full table scan due to lack of index on this column !!!!
This can cause much bigger performance issues in my opinion.
Since the values of this columns are UUIDs, then there is a very little chance of conflicted values. In this case I would prefer not to use any locks.
Just use an unique constraint (index) on this column to prevent from inserting two duplicate values.
ALTER TABLE tbl_log ADD CONSTRAINT tbl_log_id_must_be_unique UNIQUE( id );
and then use this implementation of your function:
create or replace function checkDuplicate(in_id in varchar2) return boolean is
begin
insert into tbl_log (id,"DATE") values(in_id , sysdate);
return true;
exception when dup_val_on_index then
return false;
end;
/
In the vast majority of cases the function simply inserts a new record to the table without any delay because values are UUIDs.
In seldom cases of duplicated values, when the value is already commited in the table, the insert will immediatelly fail, without any delay.
In very very rare cases (almost impossible) when two threads are trying to simultanously insert the same UUID, the second thread will be held on INSERT command and will wait some time until the first thread will commit or rollback.
As per your condition, since you are reluctant to use Primary key data integrity enforcement( which will lead to data corruption anyhow ), i would suggest that you can use MERGE statment and keep an audit log for the latest thread updating the table. This way you will be able to eliminate the entry of duplicate record as well as keep a track of when and from which thread (latest info) the id got updated. Hope the below snippet helps.
---Create dummy table for data with duplicates
DROP TABLE dummy_hist;
CREATE TABLE dummy_hist AS
SELECT LEVEL COL1,
'AVRAJIT'
||LEVEL COL2,
SYSTIMESTAMP ACTUAL_INSERTION_DT,
SYSTIMESTAMP UPD_DT,
1 thread_val
FROM DUAL
CONNECT BY LEVEL < 100;
--Update upd_dt
UPDATE dummy_hist SET upd_dt = NULL,thread_val = NULL;
SELECT * FROM dummy_hist;
--Create function
CREATE OR REPLACE
FUNCTION checkDuplicate(
in_id IN VARCHAR2,
p_thread_val IN NUMBER)
RETURN BOOLEAN
IS
cnt NUMBER;
BEGIN
MERGE INTO dummy_hist A USING
(SELECT in_id VAL FROM dual
)B ON (A.COL1 = B.VAL)
WHEN MATCHED THEN
UPDATE
SET a.upd_dt = systimestamp,
a.thread_val = p_thread_val
WHERE a.col1 = b.val WHEN NOT MATCHED THEN
INSERT
(
a.col1,
a.col2,
a.actual_insertion_dt,
a.UPD_DT,
a.thread_val
)
VALUES
(
b.val,
'AVRAJIT',
SYSTIMESTAMP,
NULL,
p_thread_val
);
COMMIT;
RETURN true;
END;
/
--Execute the fucntion
DECLARE
rc BOOLEAN;
BEGIN
FOR I IN
(SELECT LEVEL LVL FROM DUAL CONNECT BY LEVEL BETWEEN 8 AND 50
)
LOOP
rc:=checkduplicate(I.LVL,3);
END LOOP;
END;
/

Update same table after Insert trigger

I am working on a product in which I have to send SMS to concerned person when someone waits for more than 15 minutes for being served.
For that I have written a procedure that watches a table and stores CUST_ID, CUST_CATEGORY, DURATION in a separate table when the Duration exceeds 15. The table structure of this table is:
Some_Table
CUST_ID CUST_CATEGORY DURATION SMS_STATUS
I wrote a trigger as:
Trigger
create or replace trigger kiosk_sms_trg
after insert on Some_Table
referencing new as new old as old
for each row
BEGIN
SMS_Proc#My_Server; --Procudure that generates SMS
update Some_Table set status = 'Y' where id = (select max(id) id from Some_Table where status = 'N'); --Update Table that SMS has been sent
select 'Y' into :new.status from dual;
END;
But it creates Mutation Problem. How do I resolve it? Any help would be highly appreciated. I'm using Oracle 11G.
I don't think that UPDATE is allowed on SOME_TABLE as it is currently mutating.
Why not place it right after the INSERT statement which fired the trigger in the first place?.
INSERT INTO SOME_TABLE ...
update Some_Table set status = 'Y' where id = (select max(id) id from Some_Table where status = 'N'); --Update Table that SMS has been sent
I guess this would be the right approach considering you aren't doing anything row specific in that UPDATE.
As I mentioned in the comment, Is there any particular use for this last statement in the AFTER INSERT trigger? It does have meaning in the BEFORE INSERT trigger.
select 'Y' into :new.status from dual;
You cannot update the same table in Row-Level AFTER Trigger.
Change your Row-Level AFTER INSERT trigger to row-level BFEORE INSERT trigger.
But you UPDATE stmt inside the trigger will not effect the new record being inserted.
Wonder how it can be done, this is tricky.

How to catch a unique constraint error in a PL/SQL block?

Say I have an Oracle PL/SQL block that inserts a record into a table and need to recover from a unique constraint error, like this:
begin
insert into some_table ('some', 'values');
exception
when ...
update some_table set value = 'values' where key = 'some';
end;
Is it possible to replace the ellipsis for something in order to catch an unique constraint error?
EXCEPTION
WHEN DUP_VAL_ON_INDEX
THEN
UPDATE
I'm sure you have your reasons, but just in case... you should also consider using a "merge" query instead:
begin
merge into some_table st
using (select 'some' name, 'values' value from dual) v
on (st.name=v.name)
when matched then update set st.value=v.value
when not matched then insert (name, value) values (v.name, v.value);
end;
(modified the above to be in the begin/end block; obviously you can run it independantly of the procedure too).
I suspect the condition you are looking for is DUP_VAL_ON_INDEX
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('OH DEAR. I THINK IT IS TIME TO PANIC!')
As an alternative to explicitly catching and handling the exception you could tell Oracle to catch and automatically ignore the exception by including a /*+ hint */ in the insert statement. This is a little faster than explicitly catching the exception and then articulating how it should be handled. It is also easier to setup. The downside is that you do not get any feedback from Oracle that an exception was caught.
Here is an example where we would be selecting from another table, or perhaps an inner query, and inserting the results into a table called TABLE_NAME which has a unique constraint on a column called IDX_COL_NAME.
INSERT /*+ ignore_row_on_dupkey_index(TABLE_NAME(IDX_COL_NAME)) */
INTO TABLE_NAME(
INDEX_COL_NAME
, col_1
, col_2
, col_3
, ...
, col_n)
SELECT
INDEX_COL_NAME
, col_1
, col_2
, col_3
, ...
, col_n);
This is not a great solution if your goal it to catch and handle (i.e. print out or update the row that is violating the constraint). But if you just wanted to catch it and ignore the violating row then then this should do the job.

Resources