Trigger to ensure a value cannot happen - sql

This is using Oracle SQL.
Apologies in advance as I am new to the SQL world.
I'm trying to create a simple trigger to ensure a sports event cannot happen in a certain month (we'll use December as the example). So if someone tries to insert a new row with a date in December, the trigger will prevent it.
The current table uses the DATE datatype, inserted as 'DD-MMM-YYYY' but when selected it's displayed as 'DD-MMM-YY' (I don't know why.)
Anyway, I've never made triggers before and I've tried it two ways but it bugs out because when I press ENTER on SQL Plus, it just keeps going as if I was missing a semi-colon. And I'm guessing the trigger itself is not working.
CREATE OR REPLACE TRIGGER event_test
BEFORE INSERT OR UPDATE
ON sports_event
BEGIN
IF DATE
IS 'DEC' THEN
'Sports cannot occur during December.';
END IF;
END;
I've also tried with a CASE and I could not get it to work.

I'm trying to create a simple trigger to ensure a sports event cannot happen in a certain month
[...]
The exercise that I'm trying to do this for specifically asks to create a trigger to ensure the event cannot happen in a certain month.
As this is for homework / educational purpose, here are some hints first:
First, as this was said by Mureink in his answer, remember that a CHECK CONSTRAINT is the preferred way to do data validation;
Then, as you are required to use a trigger, you will need both an INSERT trigger and an UPDATE trigger;
As you will do data validation, you need a BEFOREINSERT OR UPDATE trigger;
You will access to incoming data using the NEW. pseudo-record;
And you will reject DML statement by raising an exception.
You already have the (2) and (3) in your code. Starting from that, one complete solution might look like this:
CREATE OR REPLACE TRIGGER event_test
BEFORE INSERT OR UPDATE
ON sports_event
FOR EACH ROW WHEN (EXTRACT(MONTH FROM NEW.event_date) = 12)
BEGIN
RAISE_APPLICATION_ERROR (
num=> -20107,
msg=> 'Sports cannot occur during December.');
END;
Untested. Beware of typos !

Triggers aren't really meant for data validation. Why not use a check constraint instead?
ALTER TABLE sports_event
ADD CONSTRAINT not_in_december_ck
CHECK (TO_CHAR(event_date, 'MM') != '12')

Related

Trigger when a select occurs

i have a customer with a ERP wich i don't have programming access, and wants to do a special trigger when a item is selected. The problem is that the ItemID is nowhere kept when chosen, only when whole sale is kept, and the trigger should happen before that.
This is a novice question for sure, but this value must be kept somewhere right ?
When i do a audit do see what happens when the item is chosen inside the ERP it only does SELECT statments. Can i do a trigger based on a SELECT ?
Thank you.
It is not possible to create a trigger based on execution of a select query in PL/SQL. Trigger can be created only on INSERT, UPDATE or DELETE.
References :
https://community.oracle.com/thread/1556647?tstart=0
http://www.geekinterview.com/question_details/18571

mutating table with this trigger oracle database

Use Oracle database and need to store all rows in lower case. All INSERTand UPDATE should insert and update rows with lower case data. I wrote a trigger to do it. Read a lot of about table mutation with triggers.Very confused, will this code raise mutate error or not. Please, write here, if it will.
create or replace trigger employee_name
before update or insert on employee
for each row
begin
/* convert character values to lower case */
:new.lastname := lower( :new.lastname );
:new.firstname :=lower( :new.firstname );
end;
/
The trigger will not raise a mutating table error as you're not selecting from the table your trigger is on; you're only reassigning values, which is what triggers are designed to do.
It's worth noting that this can also be accomplished with a CHECK constraint, which will force everyone updating or inserting into the table to lower case everything:
alter table employee_name
add constraint chk_emp_name_lastname
check ( lastname = lower(lastname) )
Whilst this will increase update/insert times (not necessarily more than using a trigger) it doesn't silently change data being entered but instead complains that the data is incorrect. This is sometimes a better approach.
this code not raise exception
because you not try select from table which trigger executing
This should not raise any errors. Mutate errors occur when you read (or modify) some other rows from BEFORE trigger.
NOTE: there are situations where BEFORE trigger is fired more that once for the SAME row.
In order to guarantee consistency Oracle would have to justify, that your trigger body is "idempotent": e.i. each execution on the same input gives the same result.

creating a trigger to update a date in a table before a specific value is changed

I am tryign to create a trigger which will update a table so that when something is inserted into the table the given end date will change to set to the day before the start date of the new entry. i have created this trigger which complies but when checking it doesn't work. can anyone see the porblem with it?
CREATE TRIGGER
updatetrigger12345
BEFORE INSERT ON
shares_amount
FOR EACH ROW
BEGIN
UPDATE OF
shares_amount
SET
date_end = :NEW.date_start-1
WHERE
share_issue_id = :NEW.share_issue_id
AND
share_id= :NEW.share_id
;
END
;
/
Bad, bad, bad. You do not want to attempt to update the table on whose trigger is being executed. You just need to update the ":new" fields of interest. Maybe something like this:
CREATE TRIGGER
updatetrigger12345
BEFORE INSERT ON
shares_amount
FOR EACH ROW
BEGIN
:NEW.date_end := :NEW.date_start-1;
END
;
/
Here is a tutorial. In addition, I would try to really stay away from using trigger if not needed. There certainly are situations when they make sense; however, they can be abused and cause a lot of confusion and problems down the road. Please read this classic AskTom article The Trouble With Triggers.

Oracle - Problem creating trigger that updates another table

I've read the Oracle docs on creating triggers and am doing things exactly how it shows, however this just isn't working. My goal is to update the TPM_PROJECT table with the minimum STARTDATE appearing in the TPM_TRAININGPLAN table. Thus, every time someone updates the STARTDATE column in TPM_TRAININGPLAN, I want to update teh TPM_PROJECT table. Here's what I'm trying:
CREATE TRIGGER Trigger_UpdateTrainingDelivery
AFTER DELETE OR INSERT OR UPDATE OF STARTDATE
ON TPM_TRAININGPLAN
FOR EACH ROW WHEN (new.TRAININGPLANTYPE='prescribed')
BEGIN
UPDATE TPM_PROJECT SET TRAININGDELIVERYSTART = (SELECT MIN(TP.STARTDATE) FROM TPM_TRAININGPLAN TP WHERE TP.PROJECTID = new.PROJECTID AND TP.TRAININGPLANTYPE='prescribed')
WHERE PROJECTID = new.PROJECTID
END;
The trigger is created with no errors, but I do get a warning:
Warnings: --->
W (1): Warning: execution completed with warning
<---
Of course Oracle isn't nice enough to actually tell me what the warning is, I simply am shown that there is one.
Next, if I update the training plan table with:
UPDATE TPM_TRAININGPLAN
set STARTDATE = to_date('03/12/2009','mm/dd/yyyy')
where TRAININGPLANID=15916;
I get the error message:
>[Error] Script lines: 20-22 ------------------------
ORA-04098: trigger 'TPMDBO.TRIGGER_UPDATETRAININGDELIVERY' is invalid and failed re-validation
Script line 20, statement line 1, column 7
Any ideas what I'm doing wrong? Thanks!
A few issues in no particular order.
First, in the body of a row-level trigger, you need to use :new and :old to reference the new and old records. The leading colon is necessary. So your WHERE clause would need to be
WHERE PROJECTID = :new.PROJECTID
Second, if you are running your CREATE TRIGGER in SQL*Plus, you can get a list of the errors and warnings using the SHOW ERRORS command, i.e.
SQL> show errors
You could also query the DBA_ERRORS table (or ALL_ERRORS or USER_ERRORS depending on your privilege level) but that's not something you normally need to resort to.
Third, assuming the syntax errors get corrected, you're going to get a mutating table error if you use this logic. A row level trigger on table A (TPM_TRAININGPLAN in this case) cannot query table A because the table may be in an inconsistent state. You can work around that, as Tim shows in his article, by creating a package with a collection, initializing that collection in a before statement trigger, populating the data in the collection in a row-level trigger, and then processing the modified rows in an after statement trigger. That's a decent amount of complexity to add to the system, however, since you'll have to manage multiple different objects.
Generally, you'd be better off implementing this logic as part of whatever API you use to manipulate the TPM_TRAININGPLAN table. If that is a stored procedure, it makes much more sense to put the logic to update TPM_PROJECT in that stored procedure rather than putting it in a trigger. It is notoriously painful to try to debug an application that has a lot of logic embedded in triggers because that makes it very difficult for developers to follow exactly what operations are being performed. Alternately, you could remove the TRAININGDELIVERYSTART column from TPM_PROJECT table and just compute the minimum start date at runtime.
Fourth, if your trigger fires on inserts, updates, and deletes, you can't simply reference :new values. :new is valid for inserts and updates but it is going to be NULL if you're doing a delete. :old is valid for deletes and updates but is going to be NULL if you're doing an insert. That means that you probably need to have logic along the lines of (referencing Tim's package solution)
BEGIN
IF inserting
THEN
trigger_api.tab1_row_change(p_id => :new.projectid, p_action => 'INSERT');
ELSIF updating
THEN
trigger_api.tab1_row_change(p_id => :new.projectid, p_action => 'UPDATE');
ELSIF deleting
THEN
trigger_api.tab1_row_change(p_id => :old.projectid, p_action => 'DELETE');
END IF;
END;
As Justin Cave have suggested, you can calculate the minimum start date when you need it. It might help if you create an index on (projectid, startdate);
If you really have a lot of projects and training plans, another solution could be to create a MATERIALIZED VIEW that has all the data that you need:
CREATE MATERIALIZED VIEW my_view
... add refresh options here ...
AS
SELECT t.projectid, MIN(t.start_date) AS min_start_date
FROM TPM_TRAININGPLAN t
GROUP BY t.projectid;
(sorry, don't have Oracle running, the above code is just for the reference)

SQL Server 2005 Insert Trigger with Update Statement

I am currently not in a location to test any of this out but would like to know if this is an option so I can start designing the solution in my head.
I would like to create an insert trigger on a table. In this insert trigger, I would like to get values from the inserted virtual table and use them to UPDATE the same table. Would this work or would we enter some kind of infinite loop (even though the trigger is not for update commands).
As an example if a row was inserted (which represents a new rate/cost for a vendor) I would like to update the same table to expire the old rate/cost for that vendor. The expiration is necessary vs updating the record that already exists so a history of rates/costs can be kept for reporting purposes (not to mention that the current reporting infrastructure expects this type of thing to happen and we are migrating current reports/data to SQL Server).
Thanks!
If you have only an INSERT trigger and no UPDATE trigger then there isn't any problem, but I assume you want to catch also UPDATEs and perhaps even DELETEs.
The INSTEAD OF triggers are guaranteed not to behave recursively:
If an INSTEAD OF trigger defined on a
table executes a statement against the
table that would ordinarily fire the
INSTEAD OF trigger again, the trigger
is not called recursively
With and INSTEAD OF trigger you must do both the original INSERT and the UPDATE you desire.
This doesn't sound like it would cause any problems to me, providing you're not doing an INSERT in another UPDATE trigger.