what is wrong? everything seems fine. but....
CREATE OR REPLACE TRIGGER salary_change
BEFORE UPDATE
OF emp_salary
ON employee
FOR EACH ROW
WHEN (((NEW.emp_salary-OLD.emp_salary)/OLD.emp_salary)>0.2)
DECLARE
limit NUMBER(7);
BEGIN
limit:=:OLD.emp_salary*1.2;
RAISE_APPLICATION_ERROR (-20000,'rule violated. cannot increase beyond : '|| limit);
END;
I have errors:
ERROR at line 3:
ORA-20000: rule violated. cannot increase beyond : 3360
ORA-06512: at "SYSTEM.SALARY_CHANGE", line 5
ORA-04088: error during execution of trigger 'SYSTEM.SALARY_CHANGE'
The ORA-06512 just tells you what line in your code caused the error. In this case it's line #5 in the trigger, which apparently corresponds to the RAISE_APPLICATION_ERROR call.
So, apparently ((NEW.emp_salary - OLD.emp_salary) / OLD.emp_salary is greater than 0.2. Given that the limit value from the message is 3360, this would mean that OLD.EMP_SALARY was 2800, and NEW.EMP_SALARY was greater than 3360.
Best of luck
USING RAISE_APPLICATION_ERROR ALSO GIVES ERROR ORA-06512 AND POSSIBLY ORA-04088
CREATE OR REPLACE TRIGGER hr.salary_change
before UPDATE
OF salary
ON hr.employees
FOR EACH ROW
WHEN (((NEW.salary-OLD.salary)/OLD.salary)>0.2)
DECLARE
limit NUMBER(8,2);
salary_high exception;
pragma exception_init(salary_high ,-20001);
BEGIN
limit:=:OLD.salary*1.2;
RAISE_APPLICATION_ERROR (-20001,' rule violated. cannot increase beyond :'||to_char(limit));
exception
when salary_high then
:NEW.salary:=:OLD.salary;
dbms_output.put_line(dbms_utility.format_error_stack);
END;
SQL> SET SERVEROUT ON
SQL> select salary from hr.employees where employee_id=198;
SALARY
----------
3900
SQL> update hr.employees set salary=5900 where employee_id=198;
ORA-20001: rule violated. cannot increase beyond :4680
1 row updated.
SQL> select salary from hr.employees where employee_id=198;
SALARY
----------
3900
SQL>
Related
CREATE OR REPLACE TRIGGER trg_week_day
BEFORE
INSERT OR UPDATE OR DELETE
ON emp
BEGIN
IF (TO_CHAR(SYSDATE, 'DY') = 'SAT' OR TO_CHAR(SYSDATE, 'DY') = 'SUN')
THEN
raise_application_error(-20000,'User: ' ||user ||' May not change employee table during the weekend.');
else
dbms_output.put_line('Today is not Weekend, Let us Work');
END IF;
END;
/
UPDATE emp
SET sal = 32000
WHERE empno= 101;
I get the following error
Trigger created.
ORA-20000: User: APEX_PUBLIC_USER May not change employee table during the weekend.
ORA-06512: at "SQL_XDFWSAJBEIBDLNRPNGQWTGKQO.TRG_WEEK_DAY", line 5
ORA-06512: at "SYS.DBMS_SQL", line 1721
Edit 1: I need to create a trigger on emp table before insert update or delete command is done on emp table on weekend.
i understand the ORA-20000: error
but I don't understand how to remove the ORA-06512: and what is it about...
Understanding the below comments I get that it is because of unhandled exceptions...is there a way to resolve it? in this code
Thank everyone for the help.
I have a problem with my sql code.
CREATE OR REPLACE TRIGGER
BEFORE UPDATE ON TABAPPOGGIO
FOR EACH ROW
DECLARE
ERRORE EXCEPTION;
begin
IF :NEW_ConteggioPrenotazioni>150
THEN RAISE ERRORE;
END IF;
exception
when errore then raise_application_error (-20002, 'Sala Piena');
end;
I created a query before, for tabappoggio:
CREATE VIEW TABAPPOGGIO
AS
SELECT COUNT(Codice) AS ConteggioPrenotazioni
FROM PRENOTAZIONI
WHERE Sala='101'
GROUP BY Codice
But it keeps telling me ORA-00903: invalid table name.
Any help, please, or suggestions? Thanks!
EDIT: SORRY!!! I accidentally deleted a comment of someone who told me I forgot the trigger name. I saw the tick mark and clicked it but... I failed. Sorry, I still need to realize how buttons work, I am pretty new here.
ANYWAY THANK YOU!!! I literally forgot the trigger name as a fool. I didn't realize, all in panic. Thank you so much!!!
You need INSTEAD OF trigger if you want to create a trigger on view.
INSTEAD OF triggers are valid for DML events on views. They are not valid for DDL or database events.
Demo
CREATE TABLE BaseTable
(ID int PRIMARY KEY IDENTITY(1,1),
Color nvarchar(10) NOT NULL,
Material nvarchar(10) NOT NULL,
ComputedCol AS (Color + Material)
);
--Create a view that contains all columns from the base table.
CREATE VIEW InsteadView
AS SELECT ID, Color, Material, ComputedCol
FROM BaseTable;
--Create an INSTEAD OF INSERT trigger on the view.
CREATE TRIGGER InsteadTrigger on InsteadView
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO BaseTable
SELECT Color, Material
FROM inserted
END;
In your case it becomes:
CREATE OR REPLACE TRIGGER InsteadTrigger on TABAPPOGGIO
INSTEAD OF UPDATE OR INSERT
DECLARE
ERRORE EXCEPTION;
begin
IF :NEW.ConteggioPrenotazioni>150
THEN RAISE ERRORE;
END IF;
exception
when errore then raise_application_error (-20002, 'Sala Piena');
end;
Note: You can read both the :OLD and the :NEW value, but you cannot write either the :OLD or the :NEW value.
You code has some syntax issues, but the main issue is that, if I understand your need, it will not do what you expect.
If I correctly interpret you need, you want to prevent inserting more than 150 rows in a table with a given value of sala.
If so you can simply build a trigger on the table to check, performing the count after having inserted one or more rows.
For example:
create table PRENOTAZIONI( sala varchar2(10), codice number);
create or replace trigger checkPrenotazioni
after insert on prenotazioni
declare
vNum number;
ERRORE EXCEPTION;
begin
select count(codice)
into vNum
from prenotazioni
where sala = '101';
--
IF vNum >150
THEN RAISE ERRORE;
END IF;
exception
when errore
then raise_application_error (-20002, 'Sala Piena');
end;
Now I insert 150 rows with sala = '101' and everything goes well:
SQL> insert into prenotazioni(sala, codice) select '101', 101 from dual connect by level <= 150;
150 rows created.
If I try to insert the 151th row, the row is not inserted and I have:
SQL> insert into prenotazioni(sala, codice) values ('101', 101);
insert into prenotazioni(sala, codice) values ('101', 101)
*
ERROR at line 1:
ORA-20002: Sala Piena
ORA-06512: at "ALEK.CHECKPRENOTAZIONI", line 15
ORA-04088: error during execution of trigger 'ALEK.CHECKPRENOTAZIONI'
SQL> select count(1) from prenotazioni;
COUNT(1)
----------
150
SQL>
Same thing if I try to insert 151 rows in one shot:
SQL> truncate table prenotazioni;
Table truncated.
SQL> select count(1) from prenotazioni;
COUNT(1)
----------
0
SQL> insert into prenotazioni(sala, codice) select '101', 101 from dual connect by level <= 151;
insert into prenotazioni(sala, codice) select '101', 101 from dual connect by level <= 151
*
ERROR at line 1:
ORA-20002: Sala Piena
ORA-06512: at "ALEK.CHECKPRENOTAZIONI", line 15
ORA-04088: error during execution of trigger 'ALEK.CHECKPRENOTAZIONI'
It seems you are thinking too complicated. You want to restrict records in the table PRENOTAZIONI, so write an insert trigger for that table.
CREATE OR REPLACE TRIGGER trg_too_many_prenotazioni
AFTER INSERT ON prenotazioni
DECLARE
v_count INTEGER;
BEGIN
select count(*) into v_count from prenotazioni where sala = 101;
IF v_count > 150 THEN
RAISE_APPLICATION_ERROR(-20002, 'sala piena');
END IF;
END trg_too_many_prenotazioni;
(And maybe you want a SALA table with a record per sala and a column for the number of allowed prenotazioni rather then hard-coding 150 for sala = 101 here.)
When I tried to update a table which contain 488000 records I got "ORA-21780: Maximum number of object durations exceeded" Error.Please advise me how to resolve this error.
SET SERVEROUTPUT ON SIZE UNLIMITED
DECLARE
v_temp NUMBER :=0;
v_id NUMBER :=0;
BEGIN
FOR fs IN (SELECT * FROM TABLE_A WHERE date < '01-NOV-15')
LOOP
v_temp := (fs.DEPARTURE - fs.ARRIVAL);
v_id :=fs.id;
UPDATE flown_sectors SET DEPARTURE = v_temp
WHERE id = v_id;
END LOOP;
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('EXCEPTION AT ' ||v_id );
END;
Error report:
ORA-21780: Maximum number of object durations exceeded.
ORA-06512: at "SYS.DBMS_OUTPUT", line 69
ORA-06512: at "SYS.DBMS_OUTPUT", line 82
ORA-06512: at "SYS.DBMS_OUTPUT", line 112
ORA-06512: at line 17
*Cause: This typically happens if there is infinite recursion in the PL/SQL function that is being executed.
*Action: User should alter the recursion condition in order to prevent infinite recursion.
As per the code in the question, if you go on insisting for PLSQL method, try for BULK Update rather than ROW_BY_ROW Update. A very simple scenario mentioned below to suffice your problem. Let me know if this helps.
DECLARE
TYPE lv
IS OBJECT
(EMPNO EMP.EMPNO%TYPE,
SAL EMP.SAL%TYPE
);
TYPE lv_tab IS TABLE OF lv
INDEX BY PLS_INTEGER;
lv_tab_emp lv_tab;
BEGIN
SELECT empno,sal BULK COLLECT INTO lv_tab_emp FROM emp;
FORALL I IN lv_tab_emp.FIRST..lv_tab_emp.LAST
UPDATE EMP_V1
SET SAL = 5100
WHERE EMPNO = lv_tab_emp(i).empno;
END IF;
END;
Yes, I ask numerous questions because I learn more here than from the books. I've created a simple code block that produces desired output but it seems to simple for the current learning block i'm in. Would this code work for anyone that attempts to update the iddonor for a donor table if the id already exists? Haven't learned procedures or functions yet but can guess that would be a more sensible method. Does what i have thus far satisfy and exception handler if a condition arises or should I add more in the declaration? Appreciate the suggestions and learning points if provided.
My Code:
BEGIN
INSERT INTO dd_donor (iddonor)
VALUES (305)
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('ID already Exists');
END;
DUP_VAL_ON_INDEX exception is raised when you try to store duplicate values in a column that is supported by a unique index.
Let's see a simple example. I have created a table with single column and made it the primary key, which will be supported by an implicit unique index.
Setup
SQL> CREATE TABLE t(ID NUMBER);
Table created.
SQL> ALTER TABLE t ADD CONSTRAINT t_uk PRIMARY KEY(ID);
Table altered.
SQL> INSERT INTO t(ID) VALUES(1);
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM t;
ID
----------
1
SQL> BEGIN
2 INSERT INTO t(ID) VALUES(1);
3 END;
4 /
BEGIN
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.T_UK) violated
ORA-06512: at line 2
So, the unique constraint is violated. Let's see how to capture DUP_VAL_ON_INDEX exception:
Test case
SQL> SET serveroutput ON
SQL> BEGIN
2 INSERT INTO t(ID) VALUES(1);
3 EXCEPTION
4 WHEN DUP_VAL_ON_INDEX THEN
5 DBMS_OUTPUT.PUT_LINE('Duplicate value on index');
6 END;
7 /
Duplicate value on index
PL/SQL procedure successfully completed.
SQL>
By the way, the DBMS_OUTPUT was only for demo purpose, ideally you wouldn't have it in your production code.
How do you write a procedure which shows that one field's value cannot be higher than another field's value, in terms of numbers. Say. an employee'a salary can't be higher than his manager's salary. I've never done one before
There is no declarative way to enforce business rules like this in SQL. So it has to be done with code. There are a number of gotchas, not the least of which is identifying all the scenarios where the rule needs to be enforced..
Here are the scenarios:
When we insert an employee we need to check whether their salary is greater than 90% of their manager's salary.
When we update an employee's salary we need to check that it still isn't greater than 90% of their manager's salary.
When we update a manager's salary we need to check that it is still greater than 110% of all their subordinates' salaries.
If we insert records simultaneously for a manager and their subordinates (say using INSERT ALL) we need to make sure that rule is still enforced.
If we move an employee from one manager to another we need to make sure that rule is still enforced.
Here are the things which make all this harder:
Enforcing these rules involves selecting from the table we are manipulating so we cannot use BEFORE ... FOR EACH ROW triggers, due to the ORA-04088: mutating tables exceptions.
Also, selecting from the table means we cannot run in multi-user mode, because of read consistency (otherwise session #1 could go ahead with a pay increase to an employee oblivious to the fact that session #2 is currently applying a pay decrease to that employee's manager).
So, for all those reasons, the only way to enforce such business rules is to use an API; build a stored procedure and never let any process have naked DML access to the table.
The following chunk o' code enforces the rule just when updating an employee's salary. Points of interest include:
it has user-defined exceptions to identify rule violations. Really these should be defined in a package specification, so other program units can reference them.
the use of SELECT ... FOR UPDATE to lock the rows of interest.
the use of COMMIT and ROLLBACK to release the locks. In a real implementation this might be handled differently (i.e. by the calling program).
create or replace procedure change_emp_sal
( p_eno in emp.empno%type
, p_new_sal in emp.sal%type )
is
type emp_nt is table of emp%rowtype;
l_emp emp%rowtype;
l_mgr emp%rowtype;
l_subords emp_nt;
l_idx pls_integer;
x_mgr_not_paid_enough exception;
pragma exception_init(x_mgr_not_paid_enough, -20000);
x_sub_paid_too_much exception;
pragma exception_init(x_sub_paid_too_much, -20001);
begin
-- lock the employee record
select * into l_emp
from emp
where empno = p_eno
for update of sal;
-- lock their manager's record (if they have one)
if l_emp.mgr is not null
then
select * into l_mgr
from emp
where empno = l_emp.mgr
for update;
end if;
-- lock their subordinates' records
select * bulk collect into l_subords
from emp
where mgr = p_eno
for update;
-- compare against manager's salary
if l_mgr.sal is not null
and l_mgr.sal < ( p_new_sal * 1.1 )
then
raise x_mgr_not_paid_enough;
end if;
-- compare against subordinates' salaries
for i in 1..l_subords.count()
loop
if l_subords(i).sal > ( p_new_sal * 0.9 )
then
l_idx := i;
raise x_sub_paid_too_much;
end if;
end loop;
-- no exceptions raised so we can go ahead
update emp
set sal = p_new_sal
where empno = p_eno;
-- commit to free the locks
commit;
exception
when x_mgr_not_paid_enough then
dbms_output.put_line ('Error! manager only earns '||l_mgr.sal);
rollback;
raise;
when x_sub_paid_too_much then
dbms_output.put_line ('Error! subordinate earns '||l_subords(l_idx).sal);
rollback;
raise;
end change_emp_sal;
/
Here are the four employees of Deptarment 50:
SQL> select e.empno, e.ename, e.sal, m.ename as mgr_name, m.empno as mgr_no
2 from emp e join emp m on (e.mgr = m.empno)
3 where e.deptno = 50
4 order by sal asc
5 /
EMPNO ENAME SAL MGR_NAME MGR_NO
---------- ---------- ---------- ---------- ----------
8060 VERREYNNE 2850 FEUERSTEIN 8061
8085 TRICHLER 3500 FEUERSTEIN 8061
8100 PODER 3750 FEUERSTEIN 8061
8061 FEUERSTEIN 4750 SCHNEIDER 7839
SQL>
Let's try to give Billy a big raise, which should fail...
SQL> exec change_emp_sal (8060, 4500)
Error! manager only earns 4750
BEGIN change_emp_sal (8060, 4500); END;
*
ERROR at line 1:
ORA-20000:
ORA-06512: at "APC.CHANGE_EMP_SAL", line 67
ORA-06512: at line 1
SQL>
Okay, let's give Billy a smaller raise, which should succeed...
SQL> exec change_emp_sal (8060, 4000)
PL/SQL procedure successfully completed.
SQL>
Now let's try to give Steven a swingeing pay cut, which should fail...
SQL> exec change_emp_sal (8061, 3500)
Error! subordinate earns 3500
BEGIN change_emp_sal (8061, 3500); END;
*
ERROR at line 1:
ORA-20001:
ORA-06512: at "APC.CHANGE_EMP_SAL", line 71
ORA-06512: at line 1
SQL>
So let's give Steven a token pay cut, which should succeed ...
SQL> exec change_emp_sal (8061, 4500)
PL/SQL procedure successfully completed.
SQL>
Here is the new pay structure...
SQL> select e.empno, e.ename, e.sal, m.ename as mgr_name, m.empno as mgr_no
2 from emp e join emp m on (e.mgr = m.empno)
3 where e.deptno = 50
4 order by sal asc
5 /
EMPNO ENAME SAL MGR_NAME MGR_NO
---------- ---------- ---------- ---------- ----------
8085 TRICHLER 3500 FEUERSTEIN 8061
8100 PODER 3750 FEUERSTEIN 8061
8060 VERREYNNE 4000 FEUERSTEIN 8061
8061 FEUERSTEIN 4500 SCHNEIDER 7839
SQL>
So it works, as far as it goes. It only handles two of the five scenarios. Refactoring the code to satisfy the other three is left as an exercise for the reader.