I need to create a trigger that checks that the age (in years) of the participant is higher than the minimum age of the race he/ she participates in. To do this I need to get information from the participants table and the race table. I need to add this trigger to the results table
here are my tables:
CREATE TABLE race
(
RaceID int,
Location varchar2 (60),
StartDate Date,
RaceTime Date,
MinAge int,
);
CREATE TABLE participant
(
ParticipantID int,
"Name" varchar2 (60),
DateOfBirth date,
Sex varchar2 (1),
Age int
);
CREATE TABLE results
(
RaceID int not null unique,
ParticipantID int not null unique,
position number (2)
);
here is what I have created so far:
create or replace TRIGGER dob_trg
BEFORE INSERT OR UPDATE ON RESULTS
FOR EACH ROW
DECLARE
RACE_MINAGE NUMBER;
PARTICIPANT_AGE NUMBER;
BEGIN
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE PARTICIPANT
WHERE RACE.RACEID = :NEW.RACEID;
IF : NEW.AGE < : NEW.MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
enddob_trg;
However I'm getting the following error
Error(9,6): PLS-00103: Encountered the symbol ":"
Any help greatly appreciated!
Erros :
Table race - there is an extra comma at the end of you MinAge col
CREATE TABLE race
(
RaceID int,
Location varchar2 (60),
StartDate Date,
RaceTime Date,
MinAge int,
);
Inside trigger errors:
IF : NEW.AGE < : NEW.MINAGE THEN should be
IF :NEW.AGE < :NEW.MINAGE THEN
Your create trigger is on table RESULTS but the action is made on a column called AGE that belongs to the participant table .
Fix this logic before going forward.
One more error inside your trigger :
FROM RACE PARTICIPANT
add a comma between tables
Fixed trigger
-here i what i think your trigger should look like !
Compile it and give it a try
See my logic :
This query will store the values of minage using the predicate "NEW.ParticipantID"
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE, PARTICIPANT
WHERE RACE.RACEID = :NEW.ParticipantID;
This part :- if the RACE_MINAGE is bigger or equal then
IF :NEW.AGE <= RACE_MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
Complete trigger
CREATE OR REPLACE TRIGGER dob_trg
BEFORE INSERT OR UPDATE ON participant
FOR EACH ROW
DECLARE
RACE_MINAGE NUMBER;
PARTICIPANT_AGE NUMBER;
BEGIN
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE, PARTICIPANT
WHERE RACE.RACEID = :NEW.ParticipantID;
exception
when NO_DATA_FOUND then
NULL; -- or do something else you choose
IF :NEW.AGE <= RACE_MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
end;
IF : NEW.AGE < : NEW.MINAGE THEN
You should remove the whitespace after the : symbol.
IF :NEW.AGE < :NEW.MINAGE THEN
Also, it looks like you're missing a comma in your FROM clause:
FROM RACE PARTICIPANT
But that wouldn't make your code work yet, because you're testing the wrong fields in your IF statement. Also, you're not filtering the participantId.
Related
This question already has answers here:
How to enforce key uniqueness in a temporal table?
(3 answers)
Closed 11 months ago.
I have a project based on Databases (Oracle) to create a Rental shop for cars. So far it's been going well but I got stuck on a trigger that has to check the customer as well as the car between the rental as well as the return date, so that a customer who's already rented a vehicle cannot rent another and another customer cannot rent an already rented vehicle.
Rental Table:
CREATE TABLE RentAuto
(
auto_id INT,
customer_id INT,
employee_id INT,
date_rent DATE,
date_return DATE,
rent_days VARCHAR2(5)
);
Automobile Table:
CREATE TABLE Automobile
(
auto_id INTEGER NOT NULL,
year_of_production VARCHAR(10),
auto_current_km VARCHAR(20),
price_per_day VARCHAR(20),
color_id INT,
is_auto_av INT,
model_id INT,
PRIMARY KEY(auto_id)
);
Insert into the Table:
INSERT INTO RentAuto (auto_id,customer_id,employee_id,date_rent,date_return, rent_days)
VALUES(14,13,3,TO_DATE(sysdate, 'dd-mm-yyyy'), '29-03-2022','2');
The trigger I wrote below gives me an error on the IF statement and I can't figure out how to fix it.
(PLS-00201:identifier 'NEW.CUSTOMER_ID' must be declared)
create or replace TRIGGER RENTINGTRIGGER
BEFORE INSERT OR UPDATE OF AUTO_ID,CUSTOMER_ID,DATE_RENT,DATE_RETURN ON RENTAUTO
FOR EACH ROW
BEGIN
IF RENTAUTO.CUSTOMER_ID = :NEW.CUSTOMER_ID
and ((new.DATE_RENT >= RentAuto.DATE_RETURN and new.DATE_RENT < RentAuto.DATE_RETURN)
or (new.DATE_RETURN > RentAuto.DATE_RENT and new.DATE_RETURN < RentAuto.DATE_RETURN))
THEN
RAISE_APPLICATION_ERROR(-2099, 'You can only book one car per single customer a day');
IF RentAuto.AUTO_ID = :NEW.AUTO_ID
and
((new.date_rent >= RentAuto.date_return and new.date_rent < RentAuto.date_return)
or (new.date_return > RentAuto.date_rent and new.date_return < RentAuto.date_return))
THEN
RAISE_APPLICATION_ERROR(-2099, 'Car has already been rented by another customer!');
END IF;
END IF;
END;
Here is a simple after statement trigger, that throws an exception when an insert or update leads to a customer booking more than one car a day or a car getting booked more than once a day.
If the insert or update statement is on more than one row, the trigger doesn't tell us which booking led to failure. Also, each time an insert or update occurs we must scan the whole table for clashes. If you only want to react on the row(s) being inserted or updated, you need a compound trigger, where you remember the rows in question in the "after each row" part and then compare them to the other rows in the table in the "after statement" part.
CREATE OR REPLACE TRIGGER rentingtrigger
AFTER INSERT OR UPDATE OF auto_id, customer_id, date_rent, date_return ON rentauto
v_count INTEGER;
BEGIN
select count(*)
into v_count
from rentauto
where exists
(
select null
from rentauto other
where other.customer_id = rentauto.customer_id
and other.date_rent <= rentauto.date_return
and other.date_return >= rentauto.date_rent
and other.rowid <> rentauto.rowid
);
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20099, 'You can only book one car per single customer a day');
END IF;
select count(*)
into v_count
from rentauto
where exists
(
select null
from rentauto other
where other.auto_id = rentauto.auto_id
and other.date_rent <= rentauto.date_return
and other.date_return >= rentauto.date_rent
and other.rowid <> rentauto.rowid
);
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20099, 'Car has already been rented by another customer!');
END IF;
END rentingtrigger;
I am not sure if using the create or replace function is the right way to do this but i am trying to figure out how to ONLY add record to the table when the count of employee ID where month of read date = month of sysdate is not more than 10.
I have a employee_read table using primary key, reading_ID, foreign key emp_id and an attribute of read_date.
The information i found online shows return value. but how do i add into table instead of return? is it do-able?
Thank you for your help!
CREATE OR REPLACE FUNCTION AddNewReadRecord
(varEmpID IN NUMBER. varReadDate IN DATE, varWaterMeterID IN CHAR,
varCuReading IN NUMBER, varPrevReading IN Number)
RETURN
BEGIN
END
You can obviously add DML in the function but using PRAGMA AUTONOMOUS_TRANSACTION as follows:
CREATE OR REPLACE FUNCTION AddNewReadRecord
(varEmpID IN NUMBER. varReadDate IN DATE, varWaterMeterID IN CHAR, varCuReading IN NUMBER, varPrevReading IN Number)
RETURN number IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Insert into your_table (...) -- column list
Select ... -- variable list
From your_table
Where emp_id = varEmpID
And read_date = varReadDate
Group by emp_id
Having count(1) < 10;
Commit;
Return 1; -- add logic for return using exception
END;
/
I have the following table called employees
create table employees
(
eno number(4) not null primary key,
ename varchar2(30),
zip number(5) references zipcodes,
hdate date
);
And I'm trying to set a trigger on the table that will fire before an update or delete that will check the system time first to see if the time is between 12:00-13:00, if it is it will allow the insertion, otherwise prevent it.
My guess is so far:
CREATE or REPLACE TRIGGER twelve_one
BEFORE INSERT or Update ON employees
BEGIN
IF
....
ELSE
....
END;
But that's how far I've gotten into unfortunately. Can someone please help me to retrieve the system time first? Then how can I set up that IF ELSE block? And finally How to abort the transaction/insertion/update?
I'm using Oracle SQL Developer 4.02.15
Many Thanks
I assume you must declare temporary variable to set time now, and then compare the temporary variable.
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE >= '12:00:01' AND V_DATE < '13:00:00')
THEN
INSERT INTO TABLE ..
ELSE
UPDATE TABLE...
END IF;
END;
I've two tables purchases and customers, I need to update visits_made (number) in customers if time of purchase ptime (date) in purchases table is different from the already existing last_visit (date) in customers table.
Here is the trigger that I'm trying and I'm doing something terribly and shamefully wrong.
create or replace trigger update_visits_made
after insert on purchases
for each row
declare new_date purchases.ptime%type;
begin
select ptime into new_date
from purchases
where purchases.ptime = :new.ptime;
if new_date = customers.last_visit then
new.last_visit=old.last_visit;
else
update customers
set visits_made=visits_made+1
where purchases.ptime=:new.ptime;
end if;
end;
/
show errors
Can anybody please tell me where I'm going wrong?
I get following errors
LINE/COL ERROR
10/15 PLS-00103: Encountered the symbol "=" when expecting one of the
following:
:= . ( # % ;
11/1 PLS-00103: Encountered the symbol "ELSE"
16/1 PLS-00103: Encountered the symbol "END"
This is a scalar assignment in PL/SQL:
new.last_visit = old.last_visit;
Not only should this be done with := but new and old should have colons before their names:
:new.last_visit := :old.last_visit;
Once you have fixed that problem, then the update will pose an issue:
update customers
set visits_made=visits_made+1
where purchases.ptime = :new.ptime;
It is unclear to me what this is supposed to be doing, so I can't suggest anything, other than pointing out that purchases is not defined.
I think somehow i get your requirement. Basically its a ticker which count the vists of user based on Login dates. I have written a snippet below which replicates the same scenario as mentioned Above. Let me know if this helps.
-- Table creation and insertion script
CREATE TABLE PURCHASES
(
P_ID NUMBER,
P_TIME DATE
);
INSERT INTO PURCHASES
SELECT LEVEL,SYSDATE+LEVEL FROM DUAL
CONNECT BY LEVEL < 10;
CREATE TABLE CUSTOMERS
(
P_ID NUMBER,
P_VISITS NUMBER
);
INSERT INTO CUSTOMERS
SELECT LEVEL,NULL FROM DUAL
CONNECT BY LEVEL < 10;
-- Table creation and insertion script
--Trigger Script
CREATE OR REPLACE TRIGGER update_purchase BEFORE
INSERT ON purchases FOR EACH row
DECLARE
new_date purchases.p_time%type;
BEGIN
BEGIN
SELECT A.P_TIME
INTO new_date
FROM
(SELECT p_time,
ROW_NUMBER() OVER(PARTITION BY P_ID ORDER BY P_TIME DESC) RNK
-- INTO new_date
FROM purchases
WHERE purchases.p_id = :new.p_id
)a
WHERE A.RNK =1;
EXCEPTION WHEN OTHERS THEN
RETURN;
END;
IF :NEW.P_TIME <> new_date THEN
UPDATE customers
SET P_VISITS =NVL(P_VISITS,0)+1
WHERE p_id=:new.p_id;
END IF;
END;
--Trigger Script
--Testing Script
INSERT INTO PURCHASES VALUES
(9,TO_DATE('12/11/2015','MM/DD/YYYY'));
--Testing Script
Hi there I already seen other posts with the same error code, but I can't figure it out.
I have this table 'NOLEGGIO' created in this way:
CREATE TABLE NOLEGGIO(
idNoleggio INT PRIMARY KEY,
dataNoleggio DATE,
dataRestituzione DATE,
dataRestituito DATE,
CF CHAR(16) NOT NULL,
prezzo NUMBER(4),
--SEVERAL CONSTRAINTS...
All I want to do now is a trigger that sets a 'dataRestituzione' := :NEW.dataNoleggio + INTERVAL '3' DAY; (that means returnDate := :NEW.rentalDATE ) IF the date of membership is < than a specific date.
I show you my 'TESSERATO' table (tesserato stands for membership)
CREATE TABLE TESSERATO(
numTessera INT NOT NULL UNIQUE,
dataTesseramento DATE,
dataScadenza DATE,
CF CHAR(16) PRIMARY KEY,
-- CONSTRAINT...
If I execute the query outside my trigger (coming next) it works (because I have datas in the fields i'm looking at) but if I insert this query in the trigger, it doesn't work!
This is the trigger:
CREATE OR REPLACE TRIGGER TR_NOLEGGIO
BEFORE INSERT ON NOLEGGIO
FOR EACH ROW
DECLARE
DATAT DATE;
BEGIN
:NEW.idNoleggio := id_noleggio.NEXTVAL;
SELECT T.dataTesseramento INTO DATAT
FROM NOLEGGIO N JOIN TESSERATO T ON N.CF=T.CF
WHERE DATAT < TO_DATE('27/02/2014','DD/MM/YYYY');
/* Here I've even tried to do something like:
IF DATAT < TO_DATE.... THEN . But it doesn't work either.
However the query that actually works if I execute outside the trigger is the SELECT above.
*/
:NEW.dataRestituzione := :NEW.dataNoleggio + INTERVAL '3' DAY;
END;
/
It says No data Found error, while there are datas in the rows instead!! (In fact doing the select outside the trigger matches several rows).
It's definitely driving me crazy ! Cannot understand what I do wrong.
Thank you in advance for anyone that get involved into this.
Insert staments for the two tables
-- NOLEGGIO
INSERT INTO NOLEGGIO VALUES(001,'18-OTT-2013','20-OTT-2013',NULL,'P3SDTI85A15H501H',10);
INSERT INTO NOLEGGIO VALUES(002,'15-NOV-2013','19-NOV-2013',NULL,'CNTNDR89T42F839M',700);
--idRental,dateRental,dateReturn,dateReturned,SSN,price)
-- TESSERATO
INSERT INTO TESSERATO(dataTesseramento,dataScadenza,CF) VALUES('07-set-2013','07-set-2014','RDLVRT70M08F205K');
-- SEVERAL INSERTS MORE
-- N.B. the numTessera is made with a sequence in another trigger
New Answer Following Comments
I have put together a test script for this. The new code used for the trigger seems to work correctly updating the return date if a valid membership exists within the date requirements set. Feel free to just take the trigger code and discard the rest, I have just included this as it is what I have used to verify that the trigger performs an update when it should:
CAUTION: I am dropping tables in this test to make it rerunable, so i would only recommend using the full script in a test environment
/**************** R U N O N C E ********************/
--CREATE OR REPLACE SEQUENCE id_noleggio
-- MINVALUE 0
-- MAXVALUE 1000000000
-- START WITH 1
-- INCREMENT BY 1
-- CACHE 20;
/********************************************************/
/****************** R E R U N A B L E ****************/
drop table NOLEGGIO;
drop table TESSERATO;
CREATE TABLE NOLEGGIO(
idNoleggio INT PRIMARY KEY,
dataNoleggio DATE,
dataRestituzione DATE,
dataRestituito DATE,
CF CHAR(16) NOT NULL,
prezzo NUMBER(4));
CREATE TABLE TESSERATO(
numTessera INT NOT NULL UNIQUE,
dataTesseramento DATE,
dataScadenza DATE,
CF CHAR(16) PRIMARY KEY);
-- TESSERATO
INSERT INTO TESSERATO(numTessera, dataTesseramento, dataScadenza, CF) VALUES(1, '15-NOV-2013','15-NOV-2014','ABCDEFGHI0000001');
INSERT INTO TESSERATO(numTessera, dataTesseramento, dataScadenza, CF) VALUES(2, '01-MAR-2014','01-MAR-2015','ABCDEFGHI0000002');
-- SEVERAL INSERTS MORE
-- N.B. the numTessera is made with a sequence in another trigger
CREATE OR REPLACE TRIGGER TR_NOLEGGIO
BEFORE INSERT ON NOLEGGIO
FOR EACH ROW
DECLARE
CUT_OFF_DATE DATE := TO_DATE('27/02/2014','DD/MM/YYYY');
MEMBER_EXISTS VARCHAR2(1) := 'N';
DATAT DATE;
BEGIN
:NEW.idNoleggio := id_noleggio.NEXTVAL;
-- membership exists
SELECT 'Y', T.dataTesseramento
INTO MEMBER_EXISTS, DATAT
FROM TESSERATO T
WHERE T.CF = :NEW.CF
AND T.dataTesseramento < CUT_OFF_DATE;
-- if value returned from query above is not null...
if MEMBER_EXISTS = 'Y' then
:NEW.dataRestituzione := :NEW.dataNoleggio + INTERVAL '3' DAY;
end if;
exception
when no_data_found then
-- e.g. if there are no records in the TESSERATO table with the same CF value
null; -- no action required, this will just stop an error being flagged
END;
/
-- test trigger
-- should set dataRestituzione (a valid membership exists within date requirements)
INSERT INTO NOLEGGIO VALUES(004, '01-Mar-2014', NULL, NULL, 'ABCDEFGHI0000001', 20); -- should set dataRestituzione
-- should not set dataRestituzione (membership too recent)
INSERT INTO NOLEGGIO VALUES(004, '01-Mar-2014', NULL, NULL, 'ABCDEFGHI0000002', 30);
-- should not set dataRestituzione (no record of membership in TESSERATO table)
INSERT INTO NOLEGGIO VALUES(1, '18-OCT-2013', NULL, NULL, 'P3SDTI85A15H501H', 10);
INSERT INTO NOLEGGIO VALUES(2, '15-NOV-2013', NULL, NULL, 'CNTNDR89T42F839M', 700);
--idRental,dateRental,dateReturn,dateReturned,SSN,price)
-- look at results
select * from TESSERATO;
select * from NOLEGGIO;
I think that the key problem with the way that you were trying to do this before is that you were joining to the NOLEGGIO table to retrieve data that had not yet been inserted.
Previous Answer
Try chaining the line:
WHERE DATAT < TO_DATE('27/02/2014','DD/MM/YYYY');
to:
WHERE T.dataTesseramento < TO_DATE('27/02/2014','DD/MM/YYYY');
It looks like you are using this variable for a where condition before you have assigned a value to it i.e. it doesn't know the value if DATAT until the query has completed, but you are trying to use this value within the query.