Trigger does not allow any inserts - sql

I have written a trigger that does not allow more than two 'Full' ranked professors as part of the faculty (For example, trigger should fire if a new (third) Full professor is added or rank is updated to Full for one of the existing Associate professors.)
It does compile but does not let me know add any data to my table at all. It only needs to be used once. Do I use the statement level trigger for this?
Also, at the moment it does not let me update or insert Professor Rank at all, whether its Full or Associate. How would I fix that?
I have also been told that before/after and that my logic of comparison is wrong. Please help!
Here is the trigger:
CREATE OR REPLACE TRIGGER TRG_PROF_RANK
after insert or update of F_RANK
on FACULTY
DECLARE
FULL_RANK_COUNT integer;
MAX_FULL_RANK_COUNT number :=2;
begin
select count(*) into FULL_RANK_COUNT
from FACULTY
where F_RANK ='Full';
if FULL_RANK_COUNT < MAX_FULL_RANK_COUNT
then
return;
else
if (FULL_RANK_COUNT >= MAX_FULL_RANK_COUNT) then
RAISE_APPLICATION_ERROR (-20000, 'Can only have 2 professors with ranking "Full".');
end if;
end if;
end;
/
I test it with the following statement:
insert into FACULTY values(6, 'John', 'Bonny', 'M', 13, '4079347153', 'Associate', 80000, 2, 6034, Null);
But it doesn't allow me to insert any records into the table. And this is the error that I get:
Error starting at line : 240 in command -
insert into FACULTY values(6, 'John', 'Bonny', 'M', 13, '4079347153', 'Associate', 80000, 2, 6034, Null)
Error report -
SQL Error: ORA-20000: Can only have 2 professors with ranking "Full".
ORA-06512: at "IT337104.TRG_PROF_RANK", line 16
ORA-04088: error during execution of trigger 'IT337104.TRG_PROF_RANK'
20000. 00000 - "%s"
*Cause: The stored procedure 'raise_application_error'
was called which causes this error to be generated.
*Action: Correct the problem as described in the error message or contact
the application administrator or DBA for more information.
please help, I just to make sure I can insert data.
Thank you

The query should raise a mutating table error so the trigger shouldn't be executing at all. You cannot query a table from within a trigger written on that table. You must either maintain the count of full professorships elsewhere or perform the test in your application code before issuing the insert or update. This is a problem with any data integrity check that depends on data in other rows of the same table, other than, of course, a UNIQUE constraint. It simply can't be done from within a trigger on that table.
But even if you could, you have a major logic error. If your table already contains two full professors, this trigger would prevent all Inserts and Updates on the table, even if they did not involve full professors.
But all is not lost. If you fix the logic flaw, turn the trigger into a before trigger for each row, examine the contents of :new.F_RANK and act accordingly, one solution you may want to look into can be found here: Oracle; limit rows per column value. (The second answer with the materialized view, not the first.)

Related

Oracle PL/SQL - BEFORE INSERT 'table does not exist' error?

I'm taking my first steps in Pl/SQL and am struggling with triggers. I've tried creating the trigger below but am receiving this error:
Error at line 2: PL/SQL: SQL Statement ignored
PL/SQL: ORA-00942: table or view does not exist
To clarify: I have checked the name of the table over and over, and it does exist. It is also in the same schema as the trigger I'm trying to create. The 'customer_seq.NEXTVAL' refers to a sequence created previously that runs without errors.
The code is as follows:
CREATE TRIGGER new_customer
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
INSERT INTO customer_id VALUES ('c-', customer_seq.NEXTVAL);
END;
Thanks in advance for any help.
You probably intend something like this:
CREATE TRIGGER new_customer
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
SELECT customer_seq.NEXTVAL INTO :NEW.customer_id
FROM dual;
END;
It is unclear what the purpose of 'C_' is. If it is part of the customer id, I would advise you to stick to numbers.
Also note that more recent versions of Oracle support generated always as identity -- which is much preferred over defining a sequence and trigger.

Create Instead of Insert Trigger

I am a beginner at Oracle. I am trying to create an INSTEAD OF trigger to enforce a rule that no tutor should work more than 60 hours in a month
This is what I have so far
CREATE TRIGGER limit_hour
INSTEAD OF INSERT ON SESSIONHOURS
DECLARE
totalHours NUMBER := 60;
monthOnly DATE;
totalSession NUMBER;
FOR EACH ROW
BEGIN
INSERT INTO SESSIONHOURS(SESSIONDATEKEY, TUTORKEY, TOTALSESSION)
SELECT EXTRACT (MONTH FROM DATE S.SESSIONDATEKEY), S.TOTALSESSION
INTO monthOnly, totalSession
FROM SESSIONHOURS S
END;
The error "inappropriate INTO" keeps popping up. Also I need to assign the total sum of each session(30 min each) for the extracted month and then compare it with the "totalHour". How do I assign a time value to a date value? Any suggestions would be appreciated
Rather than use an INSTEAD OF trigger, it seems to me that a BEFORE INSERT trigger would be more appropriate. INSTEAD OF triggers are commonly used to map INSERTs on non-insertable views into INSERTs into the desired tables. A BEFORE INSERT trigger, on the other hand, is fired before each row is inserted into the table, allowing the values in the row to be checked for consistency, etc. Such a trigger might be used as follows:
CREATE TRIGGER SESSIONHOURS_BI
BEFORE INSERT INTO SESSIONHOURS
FOR EACH ROW
DECLARE
nTotal_tutor_hours NUMBER;
BEGIN
SELECT SUM(HOURS)
INTO nTotal_tutor_hours
FROM SESSIONHOURS s
WHERE s.TUTORKEY = :new.TUTORKEY;
IF nTotal_tutor_hours + :new.HOURS > 60 THEN
RAISE_APPLICATION_ERROR(-20001, 'Addition of ' || :new.HOURS ||
' for tutor ' || :new.TUTORKEY ||
' exceeds monthly limit of 60');
END IF;
END SESSIONHOURS_BI;
This trigger will be fired before an INSERT into SESSIONHOURS is processed. It queries the database to determine the total number of hours worked by the tutor whose key is in the INSERT statement (:new.TUTORKEY). If the total hours worked PLUS the hours in the new record exceeds 60 an exception is raised, which causes the INSERT to be aborted. Otherwise the trigger returns normally and the INSERT proceeds.
HOWEVER - even this won't work. The problem is that the trigger is defined on the table SESSIONHOURS, and inside the trigger there is a SELECT on the SESSIONHOURS table. This will cause the database to throw the dreaded ORA-04091 exception, with explanatory text table SESSIONHOURS is mutating, trigger/function may not see it. There are several ways to fix this, the BEST of which is to follow a simple rule:
***NEVER* IMPLEMENT BUSINESS LOGIC IN A TRIGGER!!!!
A rule such as "tutors may not work more than 60 hours" is a business rule. This should be implemented in your application logic, not in a trigger. Create a procedure or function in the database to perform the INSERT INTO SESSIONHOURS and any needed validation logic, and call that procedure every time you need to insert data into SESSIONHOURS. Don't try putting the validation logic into a trigger - you'll find it's rather difficult, and will lead to never-ending debugging sessions, as noted here.
Best of luck.
Your INSERT statement is improperly written. It should be:
INSERT INTO SESSIONHOURS(SESSIONDATEKEY, TOTALSESSION)
SELECT EXTRACT (MONTH FROM DATE S.SESSIONDATEKEY), S.TOTALSESSION
FROM SESSIONHOURS S
This won't solve your "total hours" issues, but it takes care of the error you reported.
Best of luck.

oracle triggers error ORA-01427 & error ORA-04091

I have the following tables:
FACULTY table
CREATE TABLE "FACULTY"
( "FACULTY_ID" NUMBER(3,0),
"FACULTY_NAME" VARCHAR2(30),
"FACULTY_DEAN" VARCHAR2(30),
CONSTRAINT "FACULTY_PK" PRIMARY KEY ("FACULTY_ID") ENABLE
)
COURSE table
CREATE TABLE "COURSE"
( "COURSE_ID" NUMBER(5,0),
"COURSE_NAME" VARCHAR2(50),
"COURSE_LEVEL" NUMBER,
"FACULTY" NUMBER,
CONSTRAINT "COURSE_PK" PRIMARY KEY ("COURSE_ID") ENABLE
)
so now i want to achieve two things
(1) when a faculty_id is updated on the faculty table. a trigger will fire and update the corresponding rows in the course table with new faculty_id. it will also store the old faculty_id value, name of course, and the date in which the operation is performed in a course_log table.
Below is what I got
create or replace trigger update_faculty
after update on faculty
for each row
begin
insert into course_log
values (:old.faculty_id,
(select course_name
from course
where faculty=:old.faculty_id),
sysdate);
update course
set faculty=:new.faculty_id
where faculty=:old.faculty_id;
end;
But I get the following error.
error ORA-01427: single-row subquery returns more than one row ORA-06512: at "SYSTEM.UPDATE_FACULTY", line 2 ORA-04088: error during execution of trigger 'SYSTEM.UPDATE_FACULTY'
any ideas on how to solve it?
(2) Write a trigger that fires when try change the course_id attribute in the course table, which will check whether the value already exists in the course table and will update successfully if it is a new value. If the value already exists in any row, the trigger will throw an application error saying "The course_id already exists! Update not successful."
below is my query
CREATE OR REPLACE TRIGGER "UPDATE_COURSE_ID"
after update on course
for each row
declare
error number;
begin
select count(*)
into error
from course
where course_id=:new.course_id;
if error > 0 then
raise_application_error (-20000,'The course_id already found! Update not success');
end if;
if error = 0 then
update course set course_id=:new.course_id where course_id=:old.course_id;
end if;
end;
But I got this error
error ORA-04091: table SYSTEM.COURSE is mutating, trigger/function may not see it ORA-06512: at "SYSTEM.UPDATE_COURSE_ID", line 5 ORA-04088: error during execution of trigger 'SYSTEM.UPDATE_COURSE_ID'
For the first question, since you may want to insert multiple rows into the course_log table, you'd need to do something like
create or replace trigger update_faculty
after update on faculty
for each row
begin
-- I'm guessing about the definition of the course_log table
insert into course_log( faculty_id, course_name, log_date )
select :old.faculty_id, course_name, sysdate
from course
where faculty=:old.faculty_id;
update course
set faculty=:new.faculty_id
where faculty=:old.faculty_id;
end;
For the second question, it doesn't make sense to use a trigger. You'd want to use a constraint. And you already have a primary key constraint on course_id in course which is already preventing duplicate course_id values.
Enforcing this sort of thing with triggers is a really poor idea. Since a row level trigger on course cannot query the course table (with the exception of a row-level insert trigger if your insert statements are always of the single-row form INSERT ... VALUES or triggers that use autonomous transactions, neither of which is appropriate here). So if you really wanted to do this with triggers, you'd need to create a package that contained a collection of course_id values, a before statement trigger that initializes the collection, a row-level trigger that adds the :new.course_id to the collection, and an after statement trigger that iterates over the collection and looks for duplicate course_id values. That's a lot of objects to do something that shouldn't be done with triggers in the first place and that is already being done by your constraint. If you're just learning about triggers, I'm guessing that you haven't been taught about packages or collections yet which makes the solution even less appropriate.

SQL Oracle Trigger

I'm having problem with simple trigger command. This trigger operation will insert value into table address_rit when a person who studies at RIT is inserted into table person. Here's the syntax for the trigger command:
CREATE OR REPLACE TRIGGER addr
AFTER INSERT ON person
FOR EACH ROW
WHEN (NEW.college = 'RIT')
BEGIN
INSERT INTO address_rit (name, address, state)
VALUES (NEW.name, NEW.address, (SELECT name FROM states WHERE NEW.statecode = states.statecode));
END;
/
The trigger is compiled but with warning. However, further inspection shows that the trigger actually has error. Here's the error from the compilation.
PL/SQL: SQL Statement ignored ERROR
PL/SQL: ORA-00984: column not allowed here ERROR
I'm pretty sure the error is just a syntax error, but I just can't find any solution. Let me know if I need to add more detail. Thank you very much for your help.
At a minimum, you need a colon before NEW
CREATE OR REPLACE TRIGGER addr
AFTER INSERT ON person
FOR EACH ROW
WHEN (NEW.college = 'RIT')
BEGIN
INSERT INTO address_rit (name, address, state)
VALUES (:NEW.name,
:NEW.address,
(SELECT name
FROM states
WHERE :NEW.statecode = states.statecode));
END;
/
I'm also assuming that the query against the STATES table is always going to return exactly 1 row. If the database is properly normalized, though, I would expect that all the tables would have a STATECODE column rather than a STATE column and that there would be foreign keys between both PERSON and ADDRESS_RIT that reference the STATECODE column in STATES. But, if the database is properly normalized, I would also expect that you wouldn't have an ADDRESS_RIT table that duplicated the data in PERSON. Instead, ADDRESS_RIT really ought to be a view on the PERSON table.

Oracle SQL Trigger

I want to prevent the database from storing any values bigger than 20 into a table.
CREATE OR REPLACE TRIGGER Dont_Allow
AFTER INSERT ON Cities
FOR EACH ROW
WHEN (new.IDCity > 20)
BEGIN
dbms_output.put_line(' Failed to insert ' || :new.IDCity);
delete from orase where IDCity=:new.IDCity;
END;
While this does work in terms of not actually adding anything with an ID > 20, every time the trigger tries to do its magic, this shows up:
ORA-04091: table SYSTEM.ORASE is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.DONT_ALLOW", line 6
ORA-04088: error during execution of trigger 'SYSTEM.DONT_ALLOW'
What's a proper way of doing what I want?
EDIT:
I've decided to use a trigger for this:
After a new row is inserted into Employees, a trigger checks the new guy's salary and if it's above 21 units / hour, it takes 5% off management's bonus. Lame, but hey - I'm using a trigger to solve a problem I don't have: the outcome won't be pretty.
CREATE OR REPLACE TRIGGER Bite_Bonus
AFTER INSERT ON Employees
FOR EACH ROW
WHEN (new.HourSalary > 20)
BEGIN
update Management set Bonus = Bonus - 5/100 * Bonus;
END;
You shouldn't be using a TRIGGER for that, you should be using a CHECK, like CONSTRAINT city_id_below_20 CHECK (IDCity < 20). You can use ALTER TABLE ADD CONSTRAINT to put it on an existing table.
As TC1 indicated, the proper way to enforce this sort of requirement is to use a constraint.
If you are forced to use the inferior approach because this is a school assignment, you most likely want to raise an exception in your trigger
CREATE OR REPLACE TRIGGER Dont_Allow
BEFORE INSERT OR UPDATE ON Cities
FOR EACH ROW
WHEN (new.IDCity > 20)
BEGIN
RAISE_APPLICATION_ERROR( -20001, 'IDCity cannot exceed 20 so rejecting invalid value: ' || :new.IDCity );
END;
If you need to use a trigger for this, make it a BEFORE INSERT trigger, not an AFTER INSERT - you don't want that insert to happen at all. Trying to "undo" it after the fact is not a good approach.
To abort the insert, all you need to do is raise an exception within that trigger. Probably the best thing for this is to raise an application error.