I have a question about my trigger that I'm trying to create between two tables. When one table is updated the other should be updated too, but I seem to be missing proper syntax.
CREATE OR REPLACE TRIGGER TRIG_DEPT_ONUPDATE
AFTER UPDATE OF DEPT_ID ON DEPARTMENT FOR EACH ROW
BEGIN
UPDATE TEAM
SET DEPT_ID = :NEW.DEPT_ID
WHERE TEAM.DEPT_ID = :NEW.DEPT_ID;
END;
/
I get errors on update ("integrity constraint (%s.%s) violated - child record found"), but using the code:
CREATE OR REPLACE TRIGGER TRIG_DEPT_ONUPDATE
AFTER UPDATE OF DEPT_ID ON DEPARTMENT FOR EACH ROW
BEGIN
UPDATE TEAM
SET DEPT_ID = :NEW.DEPT_ID;
END;
/
it changes every single row after an update, though only select few need the change. Should an If statement be worked in somehow?
To access the newly updated row values, you need a row level trigger not a statement level trigger:
CREATE OR REPLACE TRIGGER TRIG_DEPT_ONUPDATE
AFTER UPDATE OF DEPT_ID ON TEAM
for each row
BEGIN
UPDATE DEPARTMENT
SET DEPT_ID = :NEW.DEPT_ID
Where DEPT_ID = :OLD.DEPT_ID;
END;
I guess this row
DEPT_ID = DEPT_ID - :NEW.DEPT_ID
generates some DEPT_ID which is not existing. That's the error reason.
Related
Create trigger that will
not allow update of salary for employee
who are in sales department.
Emp(no,name,dno,salary,designamtion)
Dept(dno,name,location)
Errors: TRIGGER MYTRIGGER1
Line/Col: 6/11 PLS-00049: bad bind variable 'OLD.EMP'
Line/Col: 6/31 PLS-00049: bad bind variable 'OLD.EMP'
DROP TABLE EMP;
DROP TABLE DEPT;
CREATE TABLE Dept
(
dno NUMBER PRIMARY KEY,
name VARCHAR(15),
location VARCHAR(15) );
CREATE TABLE Emp
(
no NUMBER PRIMARY KEY,
name VARCHAR(15),
dno NUMBER,
salary NUMBER,
designamtion VARCHAR(15),
FOREIGN KEY(dno) REFERENCES Dept(dno) );
insert into DEPT values (1,'SALES','GUJARAT');
insert into DEPT values (2,'MARKETING','UP');
insert into DEPT values (3,'MANUFACTURING','MP');
insert into DEPT values (4,'DEALING','VAPI');
insert into DEPT values (5,'SELL','TAPI');
insert into EMP values (1,'AMAN',2,45400,'MANAGER');
insert into EMP values (2,'BHAMAN',5,20000,'GM');
insert into EMP values (3,'CHAMAN',3,34400,'ADVISOR');
insert into EMP values (4,'DAMAN',4,75400,'WORKER');
insert into EMP values (5,'KHAMAN',1,42400,'MANAGER');
CREATE OR REPLACE trigger MYTRIGGER1
BEFORE UPDATE OF SALARY ON EMP
for each row
declare
hmmm VARCHAR(15);
begin
select Dept.name into hmmm
from Dept, Emp
where :old.emp.no=no and :old.emp.dno=Dept.dno;
dbms_output.put_line(hmmm);
end;
You don't need to alias the table the trigger is on. This will compile fine:
CREATE OR REPLACE trigger MYTRIGGER1
BEFORE UPDATE OF SALARY ON EMP
for each row
declare
hmmm VARCHAR(15);
begin
select Dept.name into hmmm
from Dept, Emp
where :old.no=no and :old.dno=Dept.dno;
dbms_output.put_line(hmmm);
end;
However, this trigger will fail when you update the table. You cannot select from the table itself in the trigger body. This will raise a mutating table error. Since it is an assignment I'll leave the research up to you.
As Koen said, your code will suffer from mutating table error.
Lucky you - you don't have to select from the table you're just updating (the emp table, right?) - you have everything you need in :new.dno. So:
SQL> create or replace trigger mytrigger1
2 before update of salary on emp
3 for each row
4 declare
5 hmmm varchar2(15);
6 begin
7 select d.name
8 into hmmm
9 from dept d
10 where d.dno = :new.dno;
11
12 dbms_output.put_line('Department name = ' || hmmm);
13 end;
14 /
Trigger created.
Testing:
SQL> set serveroutput on
SQL>
SQL> update emp set salary = 1000 where no = 1;
Department name = MARKETING
1 row updated.
SQL> update emp set salary = 1000;
Department name = MARKETING
Department name = SELL
Department name = MANUFACTURING
Department name = DEALING
Department name = SALES
5 rows updated.
SQL>
Your request is a trigger that disallows certain action for employees in Sales. Your trigger would not do so even it compiled. Two problems:
dbms_output does not prevent anything it merely 'prints' a message.
it prevents any update to salary, except when emp.dno or emp.no is also updated.
First (getting up on soapbox). You are enforcing a business rule. This should not be done in a trigger. It should be done in either the business rules engine of your application or in a database constraint or if you want to be super cautions both. Now a trigger will do the enforcement but business rule violations should glaringly obvious and easy to find, in a trigger becomes a side effect of an action, hidden away and difficult to find. (OK enough of that getting off soapbox).
The prevention method your looking for is raise_application_error. And you want a little code as possible in a trigger. In this case you have everything needed from the EMP table through the pesudo rows :old and :new so there is no reason to join your tables. However you do need to look at the specific department. It is too bad Oracle constrains the Exists predicate to introduction of a sub-select, this would be a perfect place for the structure:
If EXISTS (selects ...) then ...
But that is not available. The following uses a technique of reversing the requirement. That is it selects what is NOT wanted then if found it raise the exception, and if not found it suppress the Oracle raised error. So:
create or replace trigger mytrigger1
before update of salary on emp
for each row
declare
hmmm varchar2(01);
begin
select null
into hmmm
from dept d
where d.dno = :old.dno
and d.name = 'SALES';
raise_application_error(-20199, 'Can not update salary for Employee in Sales.');
exception
when no_data_found then null;
end mytrigger1;
This is sometimes referred to as "Programming the Exception" and is not a generally recommended procedure but at times it is useful. See fiddle here.
There is an outstanding question not addressed here. Can an employee in Sales have a salary change if they are also transferring departments. I.e. should the following succeed or fail?
update emp e
set dno = 2
, salary = salary + 5000
where e.no = 5;
How do I write a simple trigger to check when salary is updated it is not over 5% for an existing faculty member.
I have a block of code here.
create or replace TRIGGER TRG_Not_over_5per
BEFORE UPDATE OF F_SALARY ON FACULTY
DECLARE
sal FACULTY.F_SALARY%TYPE;
BEGIN
SELECT FACULTY.F_SALARY INTO sal FROM FACULTY
-- how do I use the WHERE CLAUSE here so that I get the salary affected
-- do some math on the first and second salary
-- if less than 5%, keep update, if not, reject update.
END;
Thanks in advance.
You don't need to use a SELECT in the trigger (*). Define the trigger as FOR EACH ROW and you can have access to the old and new values of any column belonging to the table.
create or replace TRIGGER TRG_Not_over_5per
BEFORE UPDATE OF F_SALARY ON FACULTY
FOR EACH ROW
So your code would look like:
if :new.f_salary < :old.f_salary * 1.05 then
raise_application_error (
-20000
, 'salary increase must be at least 5%'
);
end if;
This way of handling the rule violation is just a suggestion. You do whatever you need to. You don't need to handle the ELSE branch: Oracle will apply the update by default.
(*) In fact Oracle will hurl a mutating table exception if you do try to execute the query you want to write. Find out more.
I'm getting the following errors
Errors for TRIGGER TRIG:
LINE/COL ERROR
-------- -----------------------------------------------------------------
11/1 PL/SQL: SQL Statement ignored
11/37 PL/SQL: ORA-00911: invalid character
Actual Question is:
Consider the following relation schemas
Emp1
empid name salary dno
Del_History
dno Rows_deleted Date1
Write a PL/SQL block to delete records of all employees who belong to a particular department and then record the dno, no of rows deleted and date on which deletion occurred in the Del_History table.
create or replace trigger trig after delete on emp1 for each row
declare
d number:=&d;
begin
if deleting then
update Del_History set dno=d;
update Del_History set date1=sysdate;
update Del_History set Rows_deleted=%rowcount;
end if;
delete from emp1 where dno=d;
end;
This may not be answering your question directly but some issues with the trigger as posted are:
Your trigger will execute after delete on the table for each row. There is no need to include a delete statement in your trigger. The delete has already happened.
To access column values of the deleted row use :old.column.
Since this is a row level trigger the value of sql%rowcount will always be 1.
If deleting is not necessary since the trigger is only an after delete trigger.
create or replace trigger trig
after delete on emp1
for each row
declare
begin
update del_history
set dno = :old.dno;
update del_history
set date1 = sysdate;
update del_history
set rows_deleted = sql%rowcount; -- always 1
end;
I don't see a need for a trigger as the actual question is "Write a PL/SQL block". Below you'll find an anonymous PL/SQL block:
A PL/SQL block is defined by the keywords DECLARE, BEGIN, EXCEPTION, and END. These keywords divide the block into a declarative part, an executable part, and an exception-handling part. Only the executable part is required.
that fullfills all the requirements.
-- Write a PL/SQL block ...
declare
v_department_number constant number := 42;
v_rows_deleted number;
begin
-- ... to delete records of all employees who belong to
-- a particular department ...
delete from emp1 where dno = v_department_number;
-- record number of deleted rows
v_rows_deleted := sql%rowcount;
-- ... and then record the dno, no of rows deleted and date on
-- which deletion occurred in the Del_History table.
insert into del_history (
dno
,rows_deleted
,date1
) values (
v_department_number
,v_rows_deleted
,sysdate
);
end;
/
I try to work with trigger here.
I have a relation like this :
salary(salaryid, userid, netsalary, reward, totalsalary)
So I want to update totalsalary everytime I insert and update (netsalary or reward), it will recount : totalsalary = netsalary + reward.
To do that, I made a function and a trigger :
CREATE FUNCTION reCount()
RETURNS TRIGGER AS $function$
BEGIN
UPDATE salary SET totalsalary = netsalary + reward;
RETURN NEW;
END;
CREATE TRIGGER updateTotalsalary
AFTER INSERT OR UPDATE
ON salary
FOR EACH ROW
EXECUTE PROCEDURE reCount();
Finally, I try to test by a query insert like this :
INSERT INTO salary(salaryid,userid,netsalary,reward,totalsalary)
VALUES (1234,123, 30,2,30);
but it run for a long time and it seem never stop. So when a try to stop it with, I got many rows of :
SQL statement "UPDATE salary SET totalsalary = netsalary + reward"
PL/pgSQL function "reCount()" line 3 at SQL statement
So what is the problem. Hope you guys can give me some suggestion?
Try:
CREATE TRIGGER updateTotalsalary
AFTER INSERT OR UPDATE
ON salary
FOR EACH ROW
WHEN (pg_trigger_depth() = 0)
EXECUTE PROCEDURE reCount();
this might be better than the pg_trigger_depth() = 0 hack:
CREATE TRIGGER updateTotalsalary
AFTER INSERT OR UPDATE OF netsalary, reward
ON salary
FOR EACH ROW
EXECUTE PROCEDURE reCount();
For UPDATE events, it is possible to specify a list of columns using
this syntax:
UPDATE OF column_name1 [, column_name2 ... ]
The trigger will only fire if at least one of the listed columns is
mentioned as a target of the UPDATE command.
though personally i'd probably go for a table without the totalsalary column and a view with it (CREATE TABLE salary_ ..., CREATE VIEW salary AS SELECT ..., (salary + reward) AS totalsalary FROM salary_).
I have been bogged in this problem for quite a long time....Can anyone help me out?
Here is the thing I want to implement:
I have a table A, A has attributes: id, count, total. Here I am required to implement such a trigger: IF the count in table A is updated, the trigger will set the total to 1.
My initial code is like this:
CREATE OR REPLACE TRIGGER tri_A AFTER UPDATE OF count ON A
FOR EACH ROW
BEGIN
UPDATE A SET total = 1 WHERE id = :new.id;
END;
/
The problem with this is the mutating table. When the table is updated, the table will be locked. I searched for the answers, I tried pragma autonomous_transaction, but I got an invalid trigger specification error. And there are other comments saying that we should try to use a combination of triggers to do this....I can't figure out how to do this
Assuming id is the primary key, you don't want to UPDATE the table. You just want to set :new.total. You'll also need to do this in a BEFORE UPDATE trigger not an AFTER UPDATE trigger.
CREATE OR REPLACE TRIGGER tri_A
BEFORE UPDATE OF count ON A
FOR EACH ROW
BEGIN
:new.total := 1;
END;