New code I'm trying to code a trigger that doesn't allow duplicate values to ensure a band isn't playing at the same time. I get the below compilation error and can't figure it out:
Pretty new to oracle, so any help would be appreciated.
First of all, you do not need a trigger to prevent duplicate records to be inserted. Create a unique index on band_playing and play_time fields and this will take care of any duplicate records.
Regarding the error message: an insert statement creates a new record, therefore there is no :old version of the record, only a :new one. You would need to execute a select to check if a row with the same values exist in the table. Also, as #Littlefoot noted in his comment, an if must be closed by an end if; statement, which is missing from your code.
Related
CREATE DEFINER=`belito`#`%` TRIGGER `sumupdate` AFTER INSERT ON `stavkaotpremnice` FOR EACH ROW UPDATE otpremnica a
SET a.ukupno =
(SELECT SUM(ukupno)
FROM stavkaotpremnice
WHERE brojotpremnice = a.brojotpremnice)
WHERE a.brojotpremnice = NEW.brojotpremnice
Im struggling doing it, made some searches but didnt go nowhere.
Is this working for you in MySql? If you tried doing this in Oracle using an "AFTER INSERT .... FOR EACH ROW" trigger, you would run into a "Table is mutating error" during the actual insert into stavkaotpremnice. This is because you cannot query the table being inserted into inside of a row-level trigger on that same table. You can do so in a statement-level trigger, but then you lose access to the :new values. A sort of workaround is to use a compound trigger where you can set a variable to the :new value in the "AFTER EACH ROW" section, and then use that variable in your update in the "AFTER STATEMENT" section, but I would highly discourage doing so because that will only work for single row inserts, and you'll get unexpected results if there's ever an insert statement that inserts multiple rows with different values for stavkaotpremnice.brojotpremnice.
I think the best approach here is not to do this in a trigger. Instead, either issue the otpremnica update in your application code and/or via a batch update some time later.
So I am trying to use triggers to basically set some rules.. If anyone has an ID number lower than 3, he will have to pay only 100 dollars, but if someone has an ID above that, he will have to pay more. I did some research and have been told to use triggers and that triggers are very useful when fetching multiple rows. So I tried doing that but it didn't work. Basically the trigger gets created but then when i try to add values, I get the following error:-
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "S.PRICTICKET", line 6
ORA-04088: error during execution of trigger 'S.PRICTICKET'
here is what i did to make the trigger:-
CREATE OR REPLACE TRIGGER PRICTICKET BEFORE INSERT OR UPDATE OR DELETE ON PAYS FOR EACH ROW ENABLE
DECLARE
V_PRICE PAYS.PRICE%TYPE;
V_ID PAYS.ID%TYPE;
V_NAME PAYS.NAME%TYPE;
BEGIN
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS;
IF INSERTING AND V_ID<3 THEN
V_PRICE:=100;
INSERT INTO PAYS(ID,NAME,PRICE) VALUES (V_ID,V_NAME,V_PRICE);
ELSIF INSERTING AND V_ID>=3 THEN
V_PRICE:=130;
INSERT INTO PAYS(ID,NAME,PRICE) VALUES (V_ID,V_NAME,V_PRICE);
END IF;
END;
and the thing is, when i execute this code, i actually do get a message saying the trigger has been compiled. but when when i try to insert values into the table by using the following code, i get the error message I mentioned above.
INSERT INTO PAYS(ID,NAME) VALUES (19,'SS');
You're getting the error you specified, ORA-01422, because you're returning more than one row with the following SELECT:
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS;
You need to restrict the result set. For example, I'll use the :NEW psuedorecord to grab the row's new ID value, which if unique, will restrict the SELECT to one row:
SELECT ID,NAME INTO V_ID,V_NAME FROM PAYS WHERE ID = :NEW.ID;
Here is the Oracle docs on using triggers: https://docs.oracle.com/database/121/TDDDG/tdddg_triggers.htm#TDDDG99934
However, I believe your trigger has other issues, please see my comments and we can discuss.
EDIT: Based on our discussion.
ORA-04088: error during execution of trigger
Using INSERT inside a BEFORE INSERT trigger on the same table will create an infinite loop. Please consider using an AFTER INSERT and change your INSERTS to UPDATES, or an INSTEAD OF INSERT.
Additionally, remove DELETE from the trigger definition. That makes no sense in this context.
Let's begin clearing up a few things. You were told "triggers are very useful when fetching multiple rows" this is, as a general rule and without additional context, false. There are 4 types of DML triggers:
Before Statement - fires 1 time for the statement regardless of the number of rows processed.
Before Row - fires once for each row processed during the statement before old and new values are merged into a single set of values. At this point you are allowed to change the values in the columns.
After Row - fires once for row processed during the statement after merging old and new values into a single set of values. At this point you cannot change the column values.
After statement - fires once for the statement regardless of the number of rows processed.
Keep in mind that the trigger is effectively part of the statement.
A trigger can be fired for Insert, Update, or Delete. But, there is no need to fire on each. In this case as suggested, remove the Delete. But also the Update as your trigger is not doing anything with it. (NOTE: there are compound triggers, but they contain segments for each of the above).
In general a trigger cannot reference the table that it is fired upon. See error ORA-04091.
If you're firing a trigger on an Insert it cannot do an insert into that same table (also see ORA-04091) and even if you get around that the Insert would fire the trigger, creating a recursive and perhaps a never ending loop - that would happen here.
Use :New.column_name and :Old.column_name as appropriate to refer to column values. Do not attempt to select them.
Since you are attempting to determine the value of a column you must use a Before trigger.
So applying this to your trigger the result becomes:
CREATE OR REPLACE TRIGGER PRICTICKET
BEFORE INSERT ON PAYS
FOR EACH ROW ENABLE
BEGIN
if :new.id is not null
if :new.ID<3 then
:new.Price :=100;
else
:new.Price := 130;
end if ;
else
null; -- what should happen here?
end if ;
END PRICTICKET ;
I am trying to do a trigger delete after I delete a row in databaseA employee table, databaseB author table with the same employeeid will be deleted as well. But after several attempts, it keep prompting trigger with errors without specifying what is the error. Here is my code. Thank you.
DELIMITER //
CREATE OR REPLACE TRIGGER employee_delete
AFTER DELETE on databaseA.employee
FOR EACH ROW
BEGIN
DELETE FROM databaseB.author
WHERE databaseB.author.employeeid = old.employeeid
END;
/
Both the posters above are create. You must finish the actual delete statement with a semicolon ";". Also, "old" and "new" must be prefixed with a colon ":". In addition, the trigger may have some issues with authorization depending on grants, synonyms and authorizations. If I were to guess, the syntax problems are the first issues. If, after that, you still experience problems, you may have a grant issue.
I have a table of people who belong to various sites. These sites can change, but don't very often. So when we create an attendance record (a learner_session object) we don't store the site. But this has cause a problem in reporting how many training hours a site has, because some people have changed sites over the years. Not by much, but we'd like to get this right.
So I've added a site_at_the_time column to the learner_session table. I want to auto-populate this with the site the person was at when they attended the session. But I'm not sure how to reference this. For some reason (I'm guessing to speed development or something) the learner_id is allowed to be null. So I'm currently planning to do an update trigger. The learner_id shouldn't ever get updated, and if it ever did somehow, the entire record would be junk so I'm not worried about it overwriting it.
The trigger I have now is
create trigger set_site_at_the_time
after update of learner_id on lrn_session
begin
:new.site_at_the_time:= (select site_id from learner who where :new.learner_id = who.learner_id);
end;
which leads me to the following error:
ORA-04082: NEW or OLD references not allowed in table level triggers
Now, I've done some research and found I need to use a FOR EACH ROW - and I'm wondering what exactly this FOR EACH ROW does - is it every row captured by the trigger? Or is it every row in the table?
Also, will this trigger when I create a record too? So if I do insert into learner_session(id,learner_id,...) values(learner_session_id_seq.nextval,1234,...) will this capture that appropriately?
And while I'm here, I might as well see if there's something else I'm doing wrong with this trigger. But I'm mainly asking to figure out what the FOR EACH ROW is supposed to do and if it triggers properly. =)
FOR EACH ROW means that the trigger will fire once for each row that is updated by your SQL statement. Without this clause, the trigger will only fire once, no matter how many rows are affected. If you want to change values as they're being inserted, you have to use FOR EACH ROW, because otherwise the trigger can't know which :new and :old values to use.
As written, the trigger only fires on update. To make it also fire upon insert, you'd need to change the definition:
CREATE TRIGGER set_site_at_the_time
BEFORE INSERT OR UPDATE OF learner_id
ON lrn_session
FOR EACH ROW
BEGIN
SELECT site_id into :new.site_at_the_time
FROM learner who
WHERE :new.learner_id = who.learner_id);
END set_site_at_the_time;
I have a table which I am trying to do an insert/update on depending on the values I am given. But the insert is not working for this particular table, yet it works for the previous tables which the script has run on.
To test this problem, I put in a few anonymous blocks into oracle's sqldeveloper which inserts or updates depending on whether a key is present. Updates seem to work fine, but when it comes to inserting a new row, nothing is inserted.
If I had this table:
COFFEE_ID TEA_ID NAME
11 100 combo 1
12 101 combo 2
13 102 combo 3
Doing this will not insert anything and will instead move on to the next anonymous block:
begin
insert into COFFEE_TEA(COFFEE_ID, TEA_ID, NAME) values (14, 103, 'combo 4');
exception when dup_val_on_index then
update ....
end;
....
I suspect it has something to do with the trigger on this table. It is a BEFORE EACH ROW trigger type, and it would do an insert data into some other table. There is no exception handling in the trigger, so I'm guessing it must fail but not report it (doesn't show up in sqldeveloper when I run the script).
My two questions would be,
When the trigger runs, what happens if the ID it's trying to insert to the other table already exists? Looks like it silently fails?
How best should I fix this? I am unsure if I can change the trigger code itself, but would it be possible to catch the error inside my anonymous block (assuming that it's actually the trigger that's causing the problem). If so, how would I know what exception to catch if it fails silently?
I removed the exception in sqldeveloper and it tells me that a unique constraint was violated. Namely that the data being inserted into the other table through the trigger is the cause.
Your additional information tells us that your trigger is hurling ORA-00001, a unique key violation. This is the error which the DUP_VAL_ON_INDEX exception handles. So it seems like your exception handler which is supposed to be dealing with key violations on COFFEE_TEA is also swallowing the exceptions from your trigger. Messy.
There are two possible solutions. One is to put decent error handling in the trigger code. The other is to use MERGE for your data loading routine.
I always prefer MERGE as a mechanism for performing upserts, because I don't like using exceptions to handle legitimate expected states. Find out more.
Ideally you should do both. Triggers are supposed to be self-contained code: imposing unhandled exceptions on routines which interact with their tables breaks the enscapsulation.
A trigger will not modify the DML process on a table. Remove the exception block, the insert will either succeed or fail with an error if COFFEE_TEA is a table.
In other words, the following script will never output 0 if COFFEE_TEA is a table:
BEGIN
INSERT INTO coffee_tea(COFFEE_ID, TEA_ID, NAME) values (14, 103, 'combo 4');
dbms_output.put_line(sql%rowcount);
END;