Trigger before delete - oracle

I created a before delete trigger:
create or replace trigger myTrigger3
before delete on emp
for each row
begin
update emp set mgr = 'Null' where mgr = :old.emp_name;
end;
Where table is
emp(emp_id integer primary key, emp_name varchar(20), mgr varchar(20))
But when I run this statement the trigger is not running.
delete from emp where emp_id = 1004;
select * from emp;
Error report -
ORA-04091: table DB20178004.EMP is mutating, trigger/function may not see it
ORA-06512: at "DB20178004.MYTRIGGER3", line 2
ORA-04088: error during execution of trigger 'DB20178004.MYTRIGGER3'

You can prefer adding a foreign key constraint with set null option instead of such a trigger. Of course you need a primary key should already been defined on emp_id column :
alter table emp
add constraint fk_mgr foreign key(mgr)
references emp(emp_id)
on delete set null;
Whenever you delete the record with an emp_id which has matching values with mgr column those will be emptied after deletion of the record with that emp_id.
But please prefer a data type(numeric) for mgr conforming with the column
emp_id such as integer to be able to define a foreign key
constraint.
Demo
By the way,
I recommend you to use soft-deletion. e.g. adding a column active to the table and
set value of it to zero whenever want to delete, and do not show the
records with active=0 on the application.
If you insisting on deletion do not filter by emp_name column, since
there can be more than one people with the common name, but using emp_id
is better by far as being unique within the table.

Related

In oracle on delete set null is not working

I have created two tables:
Create Table Dept
(Department_id number Constraint Depart_id_pk Primary Key
,Department_name varchar2(20));
Create table Emp
(Emp_id number Constraint Empl_id_pk Primary Key
,First_name varchar2(10)
,salary number
,Department_id number
,Constraint depart_id_fk Foreign Key (department_id)
References Dept (Department_id) on delete set null);
Then I have inserted some records in dept and Emp table. But when I try to drop dept table, instead of setting null in Emp.department_id column it shows error like this:
SQL> Drop Table Dept;
Drop Table Dept
*
ERROR at line 1:
ORA-02449: unique/primary keys in table referenced by foreign keys
The foreign key's clause say "on delete set null". Delete is a DML operation, and had you attempted to delete rows from the dept table, the corresponding emp rows would have been updated with a null dept_id.
But this isn't the case - you tried to drop the entire table, a DDL operation. This isn't allowed, because you'd be leaving behind constraints on the emp table that reference a table that no longer exists. If you want to drop these constraints too, you can use a cascade constraints clause:
DROP TABLE dept CASCADE CONSTRAINTS

oracle trigger after update on specific field followed by insert

I'm creating a trigger that will insert a record into another table based on an after update and certain text being referenced in the update..
CREATE OR REPLACE TRIGGER NCATSPROD_PM.DEACTIVATE_USERS
AFTER UPDATE OF "CHANGEBY_CHAR" ON NCATSPROD_PM.PM_USER_DATA
REFERENCING NEW AS "BICSUPP"
BEGIN
INSERT INTO NCATSPROD_PM.DEACTIVATED_USERS (USERNAME, EMAIL, DEACTIVATED_DATE)
VALUES (SELECT USER_ID, EMAIL, CHANGE_DATE FROM ncatsprod_pm.pm_user_data);
END;
When i try to compile this i'm getting an error
Error(1,3): PL/SQL: SQL Statement ignored
Error(1,90): PL/SQL: ORA-00936: missing expression
A little unsure where i'm going wrong with this? Go to source on the error takes me to the AFTER UPDATE line but looking at examples i don't see what's up. I tried using SQL Developer's trigger wizard and that gave me the same result
The REFERENCING clause is just used to change the names of the built-in OLD and NEW records. OLD contains the values of the row before it was updated, and NEW holds the values after it was updated. Here's an example of how you might use them.
create table PM_USER_DATA (user_id varchar2(20), email varchar2(20), change_date date, changeby_char varchar2(20));
create table DEACTIVATED_USERS (username varchar2(20), email varchar2(20), deactivated_date date);
insert into pm_user_data values ('test', 'test#test.com', sysdate, 'SOMEUSER');
create or replace trigger deactivate_users
after update of changeby_char on pm_user_data
referencing old as o1 new as n1
for each row
begin
if :n1.changeby_char = 'BICSUPP' then
insert into deactivated_users (username, email, deactivated_date)
values (:n1.user_id, :n1.email, :n1.change_date);
end if;
end;
/
update pm_user_data set changeby_char = 'BICSUPP';
select * from deactivated_users;
/* output:
USERNAME EMAIL DEACTIVATED_DATE
-------------------- -------------------- ----------------
test test#test.com 10-OCT-17
1 row selected.
*/

Oracle trigger to update a table when another table insert or update

I have two tables (master-detail) I use to record orders, I need to create a trigger that allows me to update the "TOTAL_GENERAL" field that is in the master table with the sum of subtotals in the "SUBTOTAL" field the detail table that are related to the foreign key "ID_ORDEN" but I get an error with the trigger.
tables:
CREATE TABLE "ENCABEZADO_ORDEN"
("ID_ENCABEZADO" NUMBER(10,0),
"NUMERO_ORDEN" NUMBER(10,0),
"FECHA" DATE,
"NOMBRE_CLIENTE" VARCHAR2(50),
"DIRECCION" VARCHAR2(50),
"TOTAL_GENERAL" NUMBER(10,0),
"LUGAR_VENTA" VARCHAR2(50),
CONSTRAINT "ENCABEZADO_ORDEN_PK" PRIMARY KEY ("ID_ENCABEZADO")
USING INDEX ENABLE
)
CREATE TABLE "DETALLE_ORDEN"
("ID_DETALLE" NUMBER(10,0),
"PRODUCTO" VARCHAR2(50),
"PRECIO_UNITARIO" NUMBER(10,2),
"CANTIDAD" NUMBER(10,0),
"SUBTOTAL" NUMBER(10,2),
"ID_ENCABEZADO" NUMBER(10,0),
CONSTRAINT "DETALLE_ORDEN_PK" PRIMARY KEY ("ID_DETALLE")
USING INDEX ENABLE
)
/
ALTER TABLE "DETALLE_ORDEN" ADD CONSTRAINT "DETALLE_ORDEN_FK" FOREIGN KEY ("ID_ENCABEZADO")
REFERENCES "ENCABEZADO_ORDEN" ("ID_ENCABEZADO") ENABLE
/
trigger:
create or replace TRIGGER "CALCULAR_TOTAL_GENERAL"
BEFORE INSERT OR UPDATE ON "DETALLE_ORDEN"
FOR EACH ROW
DECLARE
V_ID_ENCABEZADO NUMBER(10,0);
BEGIN
SELECT "ID_ENCABEZADO"
INTO V_ID_ENCABEZADO
FROM "ENCABEZADO_ORDEN"
WHERE "ID_ENCABEZADO" = :NEW."ID_ENCABEZADO";
UPDATE "ENCABEZADO_ORDEN"
SET "TOTAL_GENERAL" = (SELECT SUM("SUBTOTAL") FROM "DETALLE_ORDEN"
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO)
WHERE "ID_ENCABEZADO" = V_ID_ENCABEZADO;
END;
This is the error message I get when I insert or update the table "DETALLE_ORDEN":
1 error has occurred
ORA-04091: table CARLOSM.DETALLE_ORDEN is mutating, trigger/function may not see it
ORA-06512: at "CARLOSM.CALCULAR_TOTAL_GENERAL", line 9
ORA-04088: error during execution of trigger 'CARLOSM.CALCULAR_TOTAL_GENERAL'
Don't use triggers for this kind of logic (for that matter, don't use triggers ever; there's almost always a better way). Also, avoid storing redundant information in base tables whenever possible.
Far better design, with minimal impact to existing code is to
1) rename table "ENCABEZADO_ORDEN" (i.e. to "ENCABEZADO_ORDEN_TAB") and 2) disable/drop "TOTAL_GENERAL" field, and then 3) create a view with original name "ENCABEZADO_ORDEN" as:
CREATE OR REPLACE VIEW ENCABEZADO_ORDEN AS
SELECT O.*, (SELECT SUM(D.SUBTOTAL) FROM DETALLE_ORDEN D
WHERE D.ID_ENCABEZADO = O.ID_ENCABEZADO) TOTAL_GENERAL
FROM ENCABEZADO_ORDEN_TAB O;
This will ensure TOTAL_GENERAL is always correct (in fact, any efforts to set it directly to some other value via update of ENCABEZADO_ORDEN will result in immediate syntax error).
If performance is an issue (i.e. users frequently query TOTAL_GENERAL field in ENCABEZADO_ORDEN table for orders with large numbers of detail records in DETALLE_ORDEN, causing Oracle to repeatedly fetch&sum multitudes of SUBTOTALS) then use a materialized view instead of a basic view.

Triggering after an update or delete on a FK constraint between 2 tables in oracle

I have the following tables:
create table emp_test_lucian as select employee_id,last_name,first_name,department_id from employees;
ALTER TABLE emp_test_lucian
ADD PRIMARY KEY (employee_id);
create table dept_test_lucian as select department_id,department_name from departments_copy;
ALTER TABLE dept_test_lucian
ADD PRIMARY KEY (department_id);
On this tables I want to perform different operations for example: If a department gets deleted (from dept_test_lucian) I will delete all the rows in emp_test_lucian that have that department id with a trigger. This works fine when no fk between the 2 is declared with the following code :
CREATE OR REPLACE TRIGGER triger5
BEFORE UPDATE or DELETE on dept_test_lucian
FOR EACH ROW
BEGIN
IF DELETING then
delete
from emp_test_lucian
where department_id = :OLD.department_id;
else if UPDATING('department_id') then
UPDATE emp_test_lucian
set department_id = :NEW.department_id
where department_id = :OLD.department_id;
END IF;
END IF;
END;
/
What can I add to the code above to work even if I have a fk between the 2 tables like so:
ALTER TABLE emp_test_lucian
ADD CONSTRAINT fk_dep_id FOREIGN KEY(department_id) REFERENCES dept_test_lucian(department_id);
the current code returns :
Error report:
ORA-00001: unique constraint (C##LABORATOR.SYS_C009994) violated
ORA-06512: at line 2
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
You need to make clear what table is the 'parent' and what table is the 'child'.
In your example:
- Parent: dept_test_lucian
- Child: emp_test_lucian
Lets call 'dept_test_lucian': TableA
Lets call 'emp_test_lucian': TableB
I come to this conclusion since there is a CONSTRAINT on TableB.department_id" that can only have a value that exists
in "TableA.department_id"
The error message tells you that there is a 'primary key being vialated'.
Primary keys on you tables are:
- "TableA.employee_id"
- "TableB.department_id"
Apparently you are trying to insert a value in one of these columns where that value already exists in.
If '1' is already existing in "TableA.employee_id" you would get such an error.
What I also see in your trigger is:
You have a BEFORE UPDATE Trigger.
So the Trigger looks if there is an UPDATE comming on "TableA" (Parent).
Then you try to UPDATE "TableB" (child) first.
This could be tricky, since "TableB.department_id" can only have values that exist in "TableA.department_id".
If the new UPDATE value doesn't exist in "TableA.department_id", you can not UPDATE that value in "TableB.department_id"

Trigger to enter deleted entries to a new table - SQL plus

I am trying to create a trigger which will enter values into a table terminated_employees when we delete values from the nm_employees table. I have written the trigger but it does not work. Is my trigger format right? Any ideas?
CREATE TABLE nm_departments(
dept2 varchar(20),
CONSTRAINT empPK PRIMARY KEY (dept2)
);
CREATE TABLE nm_employees(
name varchar(20),
dept varchar(20),
CONSTRAINT departments FOREIGN KEY (dept) REFERENCES nm_departments (dept2)ON DELETE CASCADE
);
CREATE TABLE terminated_employees(
te_name varchar(20),
te_dept varchar(20)
);
CREATE TRIGGER term_employee AFTER DELETE ON nm_employee
FOR EACH ROW
BEGIN
INSERT INTO terminated_employees (NEW.te_name, NEW.te_dept) VALUES (OLD.name,OLD.dept)
END;
You should not be specifying the NEW. on the column names of your INSERT statement. These are the columns in the terminated_employees table, NOT the new values. i.e.
INSERT INTO terminated_employees (te_name, te_dept)
VALUES (OLD.name,OLD.dept)
You can use show errors (or show err) in SQL*Plus to see the exact error.
You have a number of problems:
Wrong table name on create trigger (missing the s)
Missing ; after instert statement
The OLD. need to have : prefix. i.e. :OLD.name

Resources