Oracle Trigger Insert/update - sql

Ok so i am working on a homework assignment.
I have three tables, Movies (title, year, length, genre, studioName, producer) StarsIn (movieTitle, starName) MovieStar (name, address, gender, birthdate)
Basically i need to make sure when someone inserts or updates something in StarsIn those changes need to happen in MovieStar also.
So far i have something like this..
CREATE OR REPLACE TRIGGER testTrig
AFTER UPDATE OR INSERT ON STARSIN
DECLARE
l_name MOVIESTAR.NAME%TYPE;
BEGIN
SELECT NAME FROM MOVIESTAR INTO l_name;
FOR EACH ROW WHEN (new.STARSIN.STARNAME NOT IN l_name)
INSERT INTO MOVIESTAR(NAME) VALUES (new.STARSIN.STARNAME);
END;
I am getting a few compiler errors
Error(4,1): PL/SQL: SQL Statement ignored
Error(4,28): PL/SQL: ORA-00933: SQL command not properly ended
Error(5,10): PLS-00103: Encountered the symbol "ROW" when expecting one of
the following: in
I am very new to oracle and I am practicing Triggers. I know that this could be easily done using foreign keys, but the assignment is to use triggers.
I could really use some help with this. I have tried like a million different ways to make this happen, with no luck.
Thanks in advance for any help/advice.

I'd suggest specifying the trigger fire for each row. I find these a lot easier.
You can do a count to see if the MovieStar.Name value already exists and then insert if it doesn't; that's similar to the approach you have above. This will fail if another user inserts the movie star between the time you check and the time you insert, but it's probably good enough for a class assignment. There are accepted no-fail approaches for this but you may not have covered them in class yet.
Try something like this; it probably incorporates everything you've covered in class so far:
CREATE OR REPLACE TRIGGER TestTrig
AFTER UPDATE OR INSERT ON STARSIN
FOR EACH ROW
DECLARE
movieStarCount NUMBER;
BEGIN
SELECT COUNT(*) INTO movieStarCount
FROM MovieStar
WHERE Name = :NEW.StarName;
IF movieStarCount = 0 THEN
INSERT INTO MovieStar (Name) VALUES (:NEW.StarName);
END IF;
END;

Related

Oracle : ORA-04091: table AVIS is mutating, trigger/function may not see it

So, I'm a french student in IT, I've got this work like 4 weeks before, and I really can't get around this problem (I mainly worked on PostGRE and it's way different for me).
I've got those two tables :
link
To resume, I've got one table LIVRES (which is book in French), where there's two attributes :
-"refl", the primary key,
-"note_moy", a calculated attribute, later on that.
And a table AVIS (Rating I guess) with :
-"refl" as part Primary key (the other part is irrelevant), and foreign key references to LIVRES
-"note", with is rating from 0 to 20.
So the idea, is that "note_moy" on LIVRES is the average of all "note" ON AVIS, where the LIVRES.refl = AVIS.refl. So a book has an average rating, and I needed to do a TRIGGER, so every time there's a UPDATE or INSERT successfull, I would use the procedure "Maj_note_moy", who take a "refl", make the AVG of "note" on AVIS, and update the "note_moy" on LIVRES with that AVG.
Here the procedure :
CREATE OR REPLACE PROCEDURE maj_note_moy(vrefl livres.refl%type) IS
v_note_moy livres.note_moy%type;
BEGIN
SELECT AVG(note) INTO v_note_moy
FROM AVIS
WHERE refl = vrefl;
UPDATE Livres SET note_moy = v_note_moy WHERE refl = vrefl;
--RETURN v_note_moy;
END;
/
When I use it with a PLSQL block (like this), it works no problem.
Now, back to the trigger, I try this :
CREATE OR REPLACE TRIGGER trigger_note_moy
AFTER INSERT ON Avis
FOR EACH ROW
BEGIN
Maj_note_moy(:new.refl);
END;
/
And here it is : "Oracle : ORA-04091: table AVIS is mutating, trigger/function may not see it"
I understand what's the problem is, that I trying to do a SELECT on the table AVIS while I insert something into it. But that's what I can't get, I want the trigger after my insert or update, so why is it an issue ? I'm pretty sure I do something like this on PostGRE, and it didn't cause any problems.
So I check, I do saw the COMPOUND TRIGGER, but I really don't understand how it works, nor if it's relevant for my problem.
As #ThorsetnKettner points out in the comments, storing data redundantly via a trigger is rarely a good architectural approach. Embed the calculation in a view or even a materialized view and then you don't have to write code to maintain the data or debug issues when your code has bugs (and in a multi-user system, aggregate values maintained by triggers will almost always have bugs).
Since you're a student, though, I'm guessing that this is part of a homework assignment. If so
CREATE OR REPLACE TRIGGER trigger_note_moy
AFTER INSERT ON Avis
FOR EACH ROW
BEGIN
SELECT AVG(note)
INTO :new.note_moy
FROM AVIS
WHERE refl = :new.refl;
END;
or
CREATE OR REPLACE FUNCTION maj_note_moy(vrefl livres.refl%type)
RETURN livres.note_moy%type
IS
v_note_moy livres.note_moy%type;
BEGIN
SELECT AVG(note) INTO v_note_moy
FROM AVIS
WHERE refl = vrefl;
RETURN v_note_moy;
END;
CREATE OR REPLACE TRIGGER trigger_note_moy
AFTER INSERT ON Avis
FOR EACH ROW
BEGIN
:new.note_moy := maj_note_moy( :new.refl );
END;
would avoid the mutating table exception without needing to use a compound trigger.

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.

SQL Statement Ignored and Missing Expression

I am trying to debug a trigger in Oracle SQL Developer, but I don't really understand the compiler's errors. I trying to follow the templates that I have seen, but it doesn't seem to be working out no matter what I try.
CREATE OR REPLACE TRIGGER audit_movie_insert_trig
REFERENCING NEW AS newRow
AFTER INSERT ON audit_movie
FOR EACH ROW
BEGIN
INSERT INTO audit_movie VALUES(audit_seq.nextval, newRow.title, newRow.m_year, newRow.m_length, newRow.genre, newRow.studio_name, newRow.producer, SYSDATE, 'Insert');
END;
The errors that I am getting are:
Error(2,7): PL/SQL: SQL Statement ignored
Error(2,83): PL/SQL: ORA-00936: missing expression
Any help would be great.
You have clauses the wrong way round, as the syntax diagram shows. You should referencing clause after the after insert clause.
You also need to have a colon before newRow references. And you seem to be inserting in the same table the trigger is against, but it looks like your main table is probably just called movie?
CREATE OR REPLACE TRIGGER audit_movie_insert_trig
AFTER INSERT ON movie
REFERENCING NEW AS newRow
FOR EACH ROW
BEGIN
INSERT INTO audit_movie (id, title, m_year, m_length, genre, studio_name,
producer, when, what)
VALUES(audit_seq.nextval, :newRow.title, :newRow.m_year, :newRow.m_length,
:newRow.genre, :newRow.studio_name, :newRow.producer, SYSDATE, 'Insert');
END;
I've guessed the column names. As mentioned in a comment, you should specify the column names to avoid issues when the table definition is modified, and it makes it more obvious if you try to insert the wrong values into the wrong columns.
.m_length,
Please watch out.. it starts with a "." .. u miss the alias name!

PL/SQL Triggers Library Infotainment System

I am trying to make a Library Infotainment System using PL/SQL. Before any of you speculate, yes it is a homework assignment but I've tried hard and asking a question here only after trying hard enough.
Basically, I have few tables, two of which are:
Issue(Bookid, borrowerid, issuedate, returndate) and
Borrower(borrowerid, name, status).
The status in Borrower table can be either 'student' or 'faculty'. I have to implement a restriction using trigger, that per student, I can issue only 2 books at any point of time and per faculty, 3 books at any point of time.
I am totally new to PL/SQL. It might be easy, and I have an idea of how to do it. This is the best I could do. Please help me in finding design/compiler errors.
CREATE OR REPLACE TRIGGER trg_maxbooks
AFTER INSERT ON ISSUE
FOR EACH ROW
DECLARE
BORROWERCOUNT INTEGER;
SORF VARCHAR2(20);
BEGIN
SELECT COUNT(*) INTO BORROWERCOUNT
FROM ISSUE
WHERE BORROWER_ID = :NEW.BORROWER_ID;
SELECT STATUS INTO SORF
FROM BORROWER
WHERE BORROWER_ID = :NEW.BORROWER_ID;
IF ((BORROWERCOUNT=2 AND SORF='STUDENT')
OR (BORROWERCOUNT=3 AND SORF='FACULTY')) THEN
ROLLBACK TRANSACTION;
END IF;
END;
Try something like this:
CREATE OR REPLACE TRIGGER TRG_MAXBOOKS
BEFORE INSERT
ON ISSUE
FOR EACH ROW
BEGIN
IF ( :NEW.BORROWERCOUNT > 2
AND :NEW.SORF = 'STUDENT' )
OR ( :NEW.BORROWERCOUNT > 3
AND :NEW.SORF = 'FACULTY' )
THEN
RAISE_APPLICATION_ERROR (
-20001,
'Cannot issue beyond the limit, retry as per the limit' );
END IF;
END;
/
There should not be a commit or rollback inside a trigger. The logical exception is equivalent to ROLLBACK
This is so ugly I can't believe you're being asked to do something like this. Triggers are one of the worst ways to implement business logic. They will often fail utterly when confronted with more than one user. They are also hard to debug because they have hard-to-anticipate side effects.
In your example for instance what happens if two people insert at the same time? (hint: they won't see the each other's modification until they both commit, nice way to generate corrupt data :)
Furthermore, as you are probably aware, you can't reference other rows of a table inside a row level trigger (this will raise a mutating error).
That being said, in your case you could use an extra column in Borrower to record the number of books being borrowed. You'll have to make sure that the trigger correctly updates this value. This will also take care of the multi-user problem since as you know only one session can update a single row at the same time. So only one person could update a borrower's count at the same time.
This should help you with the insert trigger (you'll also need a delete trigger and to be on the safe side an update trigger in case someone updates Issue.borrowerid):
CREATE OR REPLACE TRIGGER issue_borrower_trg
AFTER INSERT ON issue
FOR EACH ROW
DECLARE
l_total_borrowed NUMBER;
l_status borrower.status%type;
BEGIN
SELECT nvl(total_borrowed, 0) + 1, status
INTO l_total_borrowed, l_status
FROM borrower
WHERE borrower_id = :new.borrower_id
FOR UPDATE;
-- business rule
IF l_status = 'student' and l_total_borrowed >= 3
/* OR faculty */ THEN
raise_application_error(-20001, 'limit reached!');
END IF;
UPDATE borrower
SET total_borrowed = l_total_borrowed
WHERE borrower_id = :new.borrower_id;
END;
Update: the above approach won't even work in your case because you record the issue date/return date in the issue table so the number of books borrowed is not a constant over time. In that case I would go with a table-level POST-DML trigger. After each DML verify that every row in the table validates your business rules (it won't scale nicely though, for a solution that scales, see this post by Tom Kyte).

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.