Creating an Oracle trigger to stop people enrolling twice - sql

I am facing some problems while I was trying to create a trigger. So the question I was giving is
The event holder has noted that several participants have enrolled in multiple events in the same exhibition which they do not wish to occur. Write a trigger to prevent this issue in future exhibition.
What I have written is
CREATE OR REPLACE TRIGGER check_event_enrolment
BEFORE INSERT ON entry
FOR EACH ROW
DECLARE
new_exhibiton_date DATE;
new_part_no NUMBER;
BEGIN
new_carn_date := (SELECT to_char(carn_date, 'DD/MM/YYYY')
FROM event
WHERE event_id = :new.event_id);
IF (new_carn_date IN (SELECT TO_CHAR(carn_date, 'DD/MM/YYYY')
FROM entry NATURAL
JOIN event
WHERE part_no = :new.part_no)) THEN
raise_application_error(-20001,
'The participant has already enrolled under the same exhibition');
END IF;
END;
Assume that an exhibition can have multiple events going on, the part
new_carn_date := (SELECT to_char(carn_date, 'DD/MM/YYYY')
FROM event
WHERE event_id = :new.event_id);
is giving me error and I dont know how to fix it.
Thank you in advance for your help. If you need more background information I will be more than happy to provide it.
===============================================================
I have tried using one of the solutions provided below, the trigger can be compiled but there's error.
CREATE OR REPLACE TRIGGER check_event_enrolment BEFORE
INSERT ON entry
FOR EACH ROW
DECLARE
new_exhibition_date DATE;
BEGIN
SELECT
exhibition_date
INTO new_exhibition_date
FROM
event
WHERE
event_id = :new.event_id;
IF new_exhibition_date IN (
SELECT
exhibition_date
FROM
entry
NATURAL JOIN event
WHERE
part_no = :new.part_no
) THEN
raise_application_error(-20001, 'The participant has already enroled under the same exhibition');
END IF;
END;
But it is giving me error like
Trigger CHECK_EVENT_ENROLMENT compiled
LINE/COL ERROR
--------- ------------------------------------------------------------- 18/21 PLS-00103: Encountered the symbol "JOIN" when expecting one of the following: ) , with group having intersect minus start union where connect The symbol "," was substituted for "JOIN" to continue. Errors: check compiler log
Anyone knows what is going on?

Don't use a trigger, use a UNIQUE index:
CREATE UNIQUE INDEX entry__carn_dt__part__no__uidx
ON entry (event_id, TRUNC(carn_date), part_no);
If you must use a trigger (don't) then:
CREATE OR REPLACE TRIGGER check_event_enrolment
BEFORE INSERT ON entry
FOR EACH ROW
DECLARE
num_matches PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO num_matches
FROM entry
WHERE TRUNC(carn_date) = TRUNC(:NEW.carn_date)
AND event_id = :NEW.event_id
AND part_no = :NEW.part_no;
IF num_matches > 0 THEN
raise_application_error(
-20001,
'The participant has already enrolled under the same exhibition'
) ;
END IF;
END;
/

The syntax you are using is incorrect. The correct syntax sold be -
CREATE OR REPLACE TRIGGER check_event_enrolment BEFORE
INSERT ON entry
FOR EACH ROW
DECLARE
num_matches NUMBER;
BEGIN
SELECT COUNT(*)
INTO num_matches
FROM entry
NATURAL JOIN event
WHERE part_no = :new.part_no
AND event_id = :new.event_id;
IF (num_matches > 0) THEN
raise_application_error (-20001, 'The participant has already enroled under the same exhibition' ) ;
END IF;
END;

Related

How to fix the code for trigger that prevent the insert based on some conditions

I am trying to create a trigger that I have problems with.
The triggers work is to stop the item from inserting.
Here there are 2 tables of student and subjects.
I have to check and prevent inserting when the student has not enrolled in that subject and if he has he will start it from the next semester so currently he is not enrolled there.
Please help me with this.
CREATE OR REPLACE TRIGGER check_student
BEFORE INSERT ON subject
FOR EACH ROW
BEGIN
IF :new.student_no
AND :new.student_name NOT IN (
SELECT DISTINCT student_no,student_name
FROM student
)
OR :new.student_enrollment_date < (
select min(enrolment_date)
from student
where
student.student_name = :new.student_name
and student.student_no = :new.guest_no
group by student.student_name , student.student_no
)
AND :new.student_name = (
select distinct student_name
from student
)
AND :new.student_no = (
select distinct student_no
from student
)
THEN
raise_application_error(-20000, 'Person must have lived there');
END IF;
END;
/
I have to check and prevent inserting when the student has not enrolled in that subject and if he has he will start it from the next semester so currently he is not enrolled there.
Please help me with this.
You probably have a logical prescedence issues in your conditions, since it contains ANDs and ORs without any parentheses. Also, you are checking scalar values against subqueries that return more than one row or more than one column, this will generate runtime errors.
But overall, I think that your code can be simplified by using a unique NOT EXISTS condition with a subquery that checks if all functional conditions are satisfied at once. This should be pretty close to what you want:
create or replace trigger check_student
before insert on subject
for each row
begin
if not exists (
select 1
from student
where
name = :new.student_name
and student_no = :new.student_no
and enrolment_date <= :new.student_enrollment_date
) then
raise_application_error(-20000, 'Person must have livd there');
end if;
end;
/
Note: it is unclear what is the purpose of colum :new.guest_no, so I left it apart from the time being (I assumed that you meant :new.student_no instead).
Your code is quite unclear so I am just using the same conditions as used by you in your question to let you know how to proceed.
CREATE OR REPLACE TRIGGER CHECK_STUDENT BEFORE
INSERT ON SUBJECT
FOR EACH ROW
DECLARE
LV_STU_COUNT NUMBER := 0; --declared the variables
LV_STU_ENROLLED_FUTURE NUMBER := 0;
BEGIN
SELECT
COUNT(1)
INTO LV_STU_COUNT -- assigning value to the variable
FROM
STUDENT
WHERE
STUDENT_NO = :NEW.STUDENT_NO
AND STUDENT_NAME = :NEW.STUDENT_NAME;
-- ADDED BEGIN EXCEPTION BLOCK HERE
BEGIN
SELECT
COUNT(1)
INTO LV_STU_ENROLLED_FUTURE -- assigning value to the variable
FROM
STUDENT
WHERE
STUDENT.STUDENT_NAME = :NEW.STUDENT_NAME
AND STUDENT.STUDENT_NO = :NEW.GUEST_NO
GROUP BY
STUDENT.STUDENT_NAME,
STUDENT.STUDENT_NO
HAVING
MIN(ENROLMENT_DATE) > :NEW.STUDENT_ENROLLMENT_DATE;
EXCEPTION WHEN NO_DATA_FOUND THEN
LV_STU_ENROLLED_FUTURE := 0;
END;
-- OTHER TWO CONDITIONS SAME LIKE ABOVE, IF NEEDED
-- using variables to raise the error
IF LV_STU_COUNT = 0 OR ( LV_STU_ENROLLED_FUTURE >= 1 /* AND OTHER CONDITIONS*/ ) THEN
RAISE_APPLICATION_ERROR(-20000, 'Person must have livd there');
END IF;
END;
/
Cheers!!

Error(6,87): PLS-00103: Encountered the symbol "JOIN" when expecting one of the following/compound trigger

When I try to compile below compound trigger I got the error message. Please suggest what can be done to clear those error.
I tried to use normal trigger but its throwing ORA-04091 ERROR.
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG" FOR
INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
DECLARE
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct (PROJECT_ORACLE_CODE) INTO V_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW."C_CAPEX_CATEGORY"
AND :NEW."C_TOTAL_EURO" <= 250 ;
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END AL_PROJECT_ORACLE_CODE_TRG;
I am trying to add value for one column in table after new line inserted in that table if that column is empty. This column value I am taking from that select condition and new values which are inserted in table.
The error in your question title is the second of two; running the reformatted code in your question I see:
LINE/COL ERROR
--------- -------------------------------------------------------------
2/1 PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following: function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior before after instead
8/9 PLS-00103: Encountered the symbol "JOIN" when expecting one of the following: , ; for group having intersect minus order start union where connect
(with different line number due to reformatting). In general you should fix the first error reported first, as subsequent errors are often unhelpful and caused by side-effects of the earlier ones. That is the case here. If you fix the compound trigger syntax to address the error reported against DECLARE then the JOIN is no longer a problem either:
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG"
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct PROJECT_ORACLE_CODE INTO V_CODE
...
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
Basically, the main trigger body code you had needed to be in a BEFORE EACH ROW block, since it is a compound trigger.
Incidentally, you probably want V_CODE to be declared as varchar2 rather than varchar, or as ITIB_PROJECT_ORACLE_CODE.PROJECT_ORACLE_CODE%TYPE. If you need that variable at all; the assignment back to the :NEW field probably wants to be inside the IF block (as if that is not initially null, you set it to v_code, which is then null!), but you can also select straight into the :NEW variable. So I think you really want something more like:
create or replace TRIGGER WS5108.AL_PROJECT_ORACLE_CODE_TRG
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
I don't really see why this needs to be a compound trigger though, you aren't accessing the table the trigger is against (as you were in your previous question) and you only have a single scenario - only before insert; so a simple trigger would work here:
create or replace WS5108.TRIGGER AL_PROJECT_ORACLE_CODE_TRG
BEFORE INSERT
ON ITIB_REQUESTS
FOR EACH ROW
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW.VP_DOMAIN)
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END AL_PROJECT_ORACLE_CODE_TRG;
/
And even the query within that looks confused; you don't need the subquery:
...
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE VP.ID = :NEW.VP_DOMAIN
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
...

ORA-24344: success with compilation error - Trigger APEX

I've been working around this trigger and when I run the script it tells me the previous error message. I can't seem to figure out why it won't compile correctly, every pl/sql trigger tutorial seems to have the structure my trigger has. Code is the following:
create
or replace trigger new_artist before insert
on
Artist referencing new as nvartist declare counter number;
begin select
count( * ) into
counter
from
Performer
where
Stage_name = nvartist.Stage_name;
if counter > 0 then signal sqlstate '45000';
else insert
into
Artist
values(
nvartist.Stage_name,
nvartist.Name
);
insert
into
Performer
values(nvartist.Stage_name);
end if;
end;
It checks if the new artist already exists in its supertype (Performer), if it does exist it gives an error if it doesn't it inserts both into artist(Stage_name varchar2, Name varchar2) and Performer(Stage_name). Another subtype of Performer (and sibling to Artist) is Band(Stage_name), which in turn has a relationship with Artist. Why does the compiler yell at me for this trigger?
Thanks in advance
You may want to try this variant (I slightly modified names of your tables).
Creating tables with sample data:
CREATE table test_artist(
stage_name varchar2(100)
, name varchar2(100)
);
create table test_performer(
stage_name varchar2(100)
);
/*inserting test performer on which trigger will rise an error*/
insert into test_performer
select 'performer_1' from dual;
Creating trigger:
create or replace trigger new_artist
before insert
on TEST_ARTIST
referencing new as nvartist
for each row
declare
counter number;
begin
select count(*)
into counter
from test_performer
where Stage_name = :nvartist.Stage_name;
if counter > 0 then
--signal sqlstate '45000' ;
raise_application_error( -20001, 'No insertion with existing Performer');
else
/*you cant update the same table, in other case you'll get
ora-04091 table mutating error.
But nevertheless this values will be inserted by sql triggered this trigger.*/
--insert into test_artist values(:nvartist.Stage_name, :nvartist.Name);
insert into test_performer values(:nvartist.Stage_name);
end if;
end new_artist;
After that this insert will work, cause the is no 'performer_2' in 'test_performer' table:
insert into test_artist
select 'performer_2', 'name_2' from dual;
And this will fail:
insert into test_artist
select 'performer_1', 'name_1' from dual;

Limit data input based on count of instances in table

Oracle 12c.
I currently have a table to hold patient visits containing a physician id, patient id, and data/time of visit.
I would like to create a constraint that, upon data entry, checks whether a specific physician has 5 appointments in that given day. If the physician does have 5 appointments, no additional appointment can be added.
Is there any way to do this other than using a stored procedure for entry?
If I were to use a stored procedure (as opposed to a trigger due issues declaring a variable) I receive the following error: Error(4,8): PLS-00103: Encountered the symbol "UPDATE" when expecting one of the following: := . ( # % ; not null range default character
I am unsure if this is because I can't use a BEFORE UPDATE on a procedure. Any thoughts?
CREATE OR REPLACE PROCEDURE doc_apt_limit_5
IS
v_visit_count
BEFORE UPDATE OR INSERT ON aa_patient_visit
FOR EACH ROW
BEGIN
SELECT (COUNT(*)) INTO v_visit_count
FROM aa_patient_visit
WHERE physid = :NEW.physid
GROUP BY physid, visittime;
IF v_visit_count > 4 THEN
RAISE_APPLICATION_ERROR(-20001, 'physician is fully booked on this date');
END IF;
END;
Go with trigger. Probably the best solution in this scenario.
CREATE OR REPLACE TRIGGER doc_apt_limit_5 BEFORE
UPDATE OR
INSERT ON aa_patient_visit FOR EACH ROW
DECLARE v_visit_count PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_visit_count
FROM aa_patient_visit
WHERE physid = :NEW.physid
GROUP BY physid,
visittime;
IF v_visit_count > 4 THEN
RAISE_APPLICATION_ERROR(-20001, 'physician is fully booked on this date');
END IF;
END;

PLSQL condition statement in trigger involving 2 tables

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