How to capture constraint name using save exception in PL/SQL? - oracle

I need to perform delete in bulk from multiple tables.
These tables are having referential integrity constraint enabled.
In case if I miss out to delete any record from child table, I should be able to capture the foreign key constraint name that got violated.
I'm able to capture the foreign key constraint name if I don't use SAVE EXCEPTIONS, however if I use SAVE EXCEPTIONS I'm not able to capture the name.
e.g. To demonstrate I have created 2 tables with referential integrity.
test_parent(id number,PARENT_NAME VARCHAR2)
test_child (id number,child_NAME VARCHAR2)
id column is referenced from test_child to test_parent
I inserted few records in both and then tried to delete from test_parent without deleting from test_child to replicate the error for demonstration.
set serveroutput on <br>
declare <br>
cursor cp is select * from test_parent;<br>
type t_cp is table of cp%rowtype;<br>
tt_cp t_cp;<br>
begin<br>
select * bulk collect into tt_cp from test_parent;<br>
begin<br>
forall idx in 1..tt_cp.count save exceptions<br>
delete from TEST_PARENT where id=tt_cp(idx).id;<br>
exception<br>
when others then<br>
for i in 1..sql%bulk_exceptions.count<br>
loop<br>
dbms_output.put_line(sqlerrm(-1 * SQL%BULK_EXCEPTIONS(i).ERROR_CODE));<br>
end loop;<br>
end;<br>
exception<br>
when others then<br>
rollback;<br>
dbms_output.put_line(sqlerrm);<br>
rollback;<br>
end;<br>
/<br>
How do I capture the constraint name along with using SAVE EXCEPTIONS?
[Below output if I don't use SAVE EXCEPTIONS]
ORA-02292: integrity constraint (SYSTEM.SYS_C0014790) violated - child record found
[Below output if I use SAVE EXCEPTIONS]
ORA-02292: integrity constraint (.) violated - child record found
ORA-02292: integrity constraint (.) violated - child record found

Related

What's wrong with that AFTER DELETE TRIGGER?

I'm trying to use a trigger to clean some #OneToOne related entities upon deletion of another (via SQL). Let's suppose I have a Person and an OrgUnit both referencing some Address. That Address is mapped with an ADDRESS_ID and related foreign key constraints.
The following trigger compiles just fine:
CREATE OR REPLACE TRIGGER ON_DELETE_PERSON
AFTER DELETE ON PERSON
FOR EACH ROW
BEGIN
DELETE FROM ADDRESS WHERE ID = :OLD.ADDRESS_ID;
END;
/
But whenever I try to delete a Person the following error is thrown:
DELETE FROM PERSON WHERE ID = 21179
ORA-04091: table MY_SCHEMA.PERSON is mutating, trigger/function may not see it
ORA-06512: in "MY_SCHEMA.ON_DELETE_PERSON", row 2
ORA-04088: error during execution of trigger 'MY_SCHEMA.ON_DELETE_PERSON'
What must be changed to make this trigger work?
Within the Java code I could simply handle this with CascadeStyle.DELETE... but I want to transfer that responsibility to the database to allow proper ON DELETE CASCADE behavior for all the data.
-- CASCADE DELETE on ORGUNIT deletion...
ALTER TABLE PERSON ADD CONSTRAINT PERSON_F01
FOREIGN KEY (ORGUNIT_ID) REFERENCES ORGUNIT (ID)
ON DELETE CASCADE ENABLE VALIDATE;
-- SET NULL on ADDRESS deletion...
ALTER TABLE PERSON ADD CONSTRAINT PERSON_F02
FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS (ID)
ON DELETE SET NULL ENABLE VALIDATE;
So, deleting an ORGUNIT automatically deletes all related PERSON too... but the ADDRESS would remain in the database. The TRIGGER above is meant to handle this case.
So after a few comments the answer pops up:
The FK constraint with ON DELETE SET NULL fires back to the table and causes that exception. Looks like I can't have both...
I'll get rid of the ON DELETE definition and risk the FK constraint error in that case. The cascaded delete via the trigger has the higher value to me.

Why AFTER UPDATE (FOR EACH ROW) trigger fires before (NOT DEFERRABLE) integrity constraint checks?

I am learning triggers using Oracle 12c HR sample schema and Oracle Develop PL/SQL Program Units student guide. Oracle says that the trigger execution model is:
Execute all BEFORE STATEMENT triggers.
Loop for each row affected by the SQL statement:
Execute all BEFORE ROW triggers for that row.
Execute the DML statement AND PERFORM INTEGRITY CONSTRAINT CHECKING FOR THAT ROW.
Execute all AFTER ROW triggers for that row.
Execute all AFTER STATEMENT triggers.
The sample HR schema has an EMPLOYEES table with a DEPARTMENT_ID FK recerencing a DEPARTMENT_ID PK in a DEPARTMENTS table.
Correctly, if I try to insert a record in EMPLOYEES having a FK value that does not exist among the PKs in DEPARTMENTS, I will get a ORA-02291: integrity constraint (HR.EMP_DEPT_FK) violated - parent key not found error.
As a proof of concept, I create the following BEFORE UPDATE trigger to insert the missing record in DEPARTMENTS and avoid the integrity constraint violation:
CREATE OR REPLACE TRIGGER employee_dept_fk_trg
BEFORE UPDATE OF department_id ON employees
FOR EACH ROW
BEGIN
INSERT INTO departments (department_id, department_name) VALUES(:new.department_id,'Dept '||:new.department_id);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
NULL; -- mask exception if department already exists
END;
It works fine (I can insert a non existent FK as the corresponding PK would be created first).
The studying material says then that we could make the HR.EMP_DEPT_FK constraint DEFERRABLE INITIALLY DEFERRED in order to postpone the integrity check at the moment of the final COMMIT and not at the end of each INSERT statement (see item #4 in list), and so create an AFTER UPDATE trigger rather than a BEFORE UPDATE one like the previous.
Are there any advantages of doing so versus just using a BEFORE UPDATE trigger?
Before altering the HR.EMP_DEPT_FK constraint setting it DEFERRABLE INITIALLY DEFERRED, I wanted to verify the assertion in item #4, so I created an identical AFTER UPDATE trigger and I tried to insert a non existent FK in the employees table: I RECEIVED NO ORA-02291 ERROR and the new PK record in DEPARTMENTS table has been created! It seems that the AFTER UPDATE trigger executes before any integrity constraint check, while that should happen after! (see item #5)
Could you help me on this topic, please?
Thank you all,
Pino

Replacing Primary Key PL SQL

I am trying to write a PL/SQL procedure that will look for an existing primary key "supplier_id" from the supplier table and replacing it with a new one. The primary key "supplier_id" is also a foreign key for a few other tables. Therefore I need update the foreign key locations as well. Here is the procedure I have written to solve this:
create or replace PROCEDURE ex5b_supplier_update(supplier_id_delete IN VARCHAR2,
supplier_id_update IN VARCHAR2) IS
CURSOR supplier_cursor IS
SELECT supplier_id
FROM supplier;
supplier_row supplier_cursor%rowtype;
BEGIN
OPEN supplier_cursor;
LOOP
FETCH supplier_cursor INTO supplier_row;
EXIT WHEN supplier_cursor%notfound;
IF ex5b_supplier_exist(supplier_id_delete) THEN
UPDATE supplier
SET supplier_id = supplier_id_update
WHERE supplier_id = supplier_id_delete;
UPDATE PURCHASE_ORDER
SET supplier_id = supplier_id_update
WHERE supplier_id = supplier_id_delete;
UPDATE PRODUCT
SET supplier_id = supplier_id_update
WHERE supplier_id = supplier_id_delete;
DBMS_OUTPUT.PUT_LINE('UPDATED');
ELSE
DBMS_OUTPUT.PUT_LINE('NOT UPDATED');
END IF;
END LOOP;
CLOSE supplier_cursor;
END;
The procedure gives me the following error:
Error starting at line : 2 in command -
BEGIN
ex5b_supplier_update('S500','S600');
END;
Error report - ORA-02292: integrity constraint (SYSTEM.PRODUCT_FK)
violated - child record found ORA-06512: at
"SYSTEM.EX5B_SUPPLIER_UPDATE", line 15 ORA-06512: at line 2
02292. 00000 - "integrity constraint (%s.%s) violated - child record found"
*Cause: attempted to delete a parent key value that had a foreign
dependency.
*Action: delete dependencies first then parent or disable constraint.
Which makes total sense you cannot delete a primary key that is used as a foreign key. But I also can't change foreign keys that have no primary keys.
So my question is how can I change the supplier_id and all its foreign keys at the same time to avoid this error?
In a relational database a primary key is guaranteed to be three things:
1) Not nullable
2) Unique
3) UNCHANGING
It's the third rule which you're violating here, and from the errors you're getting perhaps you see why. This way lies madness. Do not change the value of a primary key. Change the attribute values all you like, so that the row now appears to be something completely different - but do not change the primary key. If you need think you need to change the primary key what you're really saying is that your primary key is not, in fact, primary. It might be a unique key, but it is by definition not a primary key.
Primary keys do not get changed.
Best of luck.
EDIT
If you really want to "change" the primary key without disabling constraints and etc, here's what you do:
Start a transaction.
Create a new row in your table with a new ID.
Copy all attributes EXCEPT FOR THE PRIMARY KEY ID COLUMN from the "original" row to the "new" row.
Update all rows in tables with foreign key constraints which reference the "original" row to reference the "new" row, i.e. change the "old" ID value to the "new" ID value.
Delete the "original" row.
COMMIT the transaction.
When done in this manner you don't violate any of the rules regarding primary keys, and at the end of the transaction the primary key appears to have been changed and all FK's are updated.
Best of luck.

Oracle drop constraint unique and re-add it

I've drop a constraint unique key with command:
ALTER TABLE table
DROP CONSTRAINT UNIQUE uk_nome;
it's removed because I don't see it, but when I try to re-add it with different parameter:
ALTER TABLE tale ADD CONSTRAINT UK_name UNIQUE (uk_1, uk_2);
I receive the error:
Errore SQL: ORA-00955: name is already used by an existing object.
Where is the problem?
You drop uk_nome and then add uk_name. I mean that there is a typo in uk_nome.
Resolved, the unique key is present, like index, into the system in the table dba_objects

Oracle Database with very few foreign key constraints

I've just started a new project and I am confronted with a production application Oracle 10g database that has just 3 foreign key constraints. I am not used to seeing databases with no foreign key constraints. I am guessing that there may be some performance/concurrency considerations to not using FKs. The reason is that in the logical database schema the architect has specified all the relationships, but these relationships are not implemented in the database as Foreign Key constraints.
Question: I read that I can define a Foreign Key Constraint with RELY NOVALIDATE that will not impact performance. Is it worth while to define RELY FK constraints on this database just so that the relationship can be easily seen? this application is not built using ORM, is it really worth while to do without foreign keys?
The database is denormalised with example below
Table 1 : FINProduct(ID (number), Description(varchar(5)), FINproductCode(varchar(10))...)
Table 2: FINProductCode(ID (number, FINproductCode(varchar(10)) , LastUpdated(datetime)...)
So instead of having a relationship between Tables 1 and 2 the FINproductCode column is just replicated in table 1.
It's too early to drink but I think i need one!
I would be very wary about assuming that the absence of foreign key constraints was a reasoned response to performance issues. There is an overhead to enforcing a foreign key constraint (particularly where appropriate indexes are missing) but it is incredibly unlikely that your application can validate the constraint more efficiently than Oracle can. So the question really is whether you want the small overhead of foreign key constraints or the near certainty that you will get invalid data inserted into the database. It would be extremely unlikely that this is a trade-off that you want to make-- I've yet to meet a business user that would be happy to capture incorrect and incomprehensible data even if doing so was a bit faster than capturing correct data.
Unless there is substantially more background, I would tend to create all the missing foreign key constraints. Creating RELY NOVALIDATE constraints is possible but it defeats the major benefit of foreign key constraints-- preventing invalid data from entering the database in the first place.
It depends on whether you want to add the FK only for documentation purposes or whether you want to prevent future INSERTs/UPDATEs with an invalid FK value.
If you want it only for documentation purposes, I'd create the FK constraint with RELY NOVALIDATE and DISABLE it afterwards - otherwise, Oracle will check it for future INSERTs / UPDATEs.
However: DON'T DO THIS UNLESS YOU ABSOLUTELY NEED IT!
I agree with Justin Cave: In most cases, you should just add "plain" FK constraints - this way, you can ensure that your existing data is correct.
I would try to create the constraints and report violations into a exception table. Fix the data and enable the constraint.
Create some test data
create table parent (pk integer
,data varchar2(1)
,CONSTRAINT PARENT_PK PRIMARY KEY (PK) ENABLE );
create table child (pk integer
,pk_parent integer
,data varchar2(1)
,CONSTRAINT CHILD_PK PRIMARY KEY (PK) ENABLE );
insert into parent values (1,'a');
insert into parent values (2,'b');
insert into child values (1,1,'a');
insert into child values (2,2,'b');
insert into child values (3,3,'c');
Create a foreign key constraint:
alter table child add constraint fk_parent foreign key(pk_parent) references parent(pk);
SQL Error: ORA-02298: Kan (ROB.FK_PARENT) niet valideren - bovenliggende sleutels zijn niet gevonden.
02298. 00000 - "cannot validate (%s.%s) - parent keys not found"
*Cause: an alter table validating constraint failed because the table has
child records.
*Action: Obvious
Create the foreign key with 'enable novalidate' option
alter table child add constraint fk_parent foreign key(pk_parent) references parent(pk) enable novalidate;
table CHILD altered.
insert into child values (4,4,'c');
SQL Error: ORA-02291: Integriteitsbeperking (ROB.FK_PARENT) is geschonden - bovenliggende sleutel is niet gevonden.
02291. 00000 - "integrity constraint (%s.%s) violated - parent key not found"
*Cause: A foreign key value has no matching primary key value.
*Action: Delete the foreign key or add a matching primary key.
No new data violating the FK can be inserted.
Now let's fix the data already in the table that violates the FK constraint
Create an exceptions table and try to enable the constraint:
create table exceptions(row_id rowid,
owner varchar2(30),
table_name varchar2(30),
constraint varchar2(30));
ALTER TABLE child ENABLE constraint fk_parent EXCEPTIONS INTO EXCEPTIONS;
Error report:
SQL Error: ORA-02298: Kan (ROB.FK_PARENT) niet valideren - bovenliggende sleutels zijn niet gevonden.
02298. 00000 - "cannot validate (%s.%s) - parent keys not found"
*Cause: an alter table validating constraint failed because the table has
child records.
*Action: Obvious
Check the exceptions table for problems:
select * from exceptions;
ROW_ID OWNER TABLE_NAME CONSTRAINT
------ ------------------------------ ------------------------------ ------------------------------
AABA78 ROB CHILD FK_PARENT
AAFAAA
Ow9AAC
select * from child where rowid = 'AABA78AAFAAAOw9AAC';
Fix the problem
delete from child where pk = 3;
1 rows deleted.
ALTER TABLE child ENABLE constraint fk_parent EXCEPTIONS INTO EXCEPTIONS;
table CHILD altered.
Constraint enabled and data correct

Resources