PLSQL condition statement in trigger involving 2 tables - sql

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

Related

Getting unexpected values for number of days between two dates in oracle

I am writing a SQL code which fetches two dates from the database and calculates the number of days between them. Here is the code:
create table borrower(
roll_no number,
date_of_issue date,
name_of_book varchar(20),
status varchar(10)
);
insert into borrower values(1,to_date('02-JAN-2022'),'dbms','issued');
insert into borrower values(2,to_date('10-JAN-2022'),'cns','issued');
insert into borrower values(3,to_date('17-JAN-2022'),'spos','issued');
insert into borrower values(4,to_date('26-JAN-2022'),'toc','issued');
create table fine(
roll_no number,
current_date date,
amount number
);
insert into fine values(1,to_date('14-FEB-2022'),null);
insert into fine values(2,to_date('14-FEB-2022'),null);
insert into fine values(3,to_date('14-FEB-2022'),null);
insert into fine values(4,to_date('14-FEB-2022'),null);
DECLARE
roll_counter number:=1;
initial_date date;
final_date date;
date_calc number;
BEGIN
loop
select date_of_issue into initial_date from borrower where roll_no=roll_counter;
select current_date into final_date from fine where roll_no=roll_counter;
date_calc:=final_date-initial_date;
dbms_output.put_line(date_calc);
roll_counter:=roll_counter+1;
exit when roll_counter>4;
end loop;
END;
/
drop table borrower;
drop table fine;
I am not getting any error, but instead getting unexpected values for the number of days. Here is the output:
Statement processed.
246.4165625
238.4165625
231.4165625
222.4165625
I was expecting the number of days between the two dates(check the table). Can someone help me sort this out.
CURRENT_DATE is an Oracle keyword that returns the current date. Name your column something that is not an Oracle keyword.
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CURRENT_DATE.html
As #Matthew McPeak pointed out, CURRENT_DATE is a built-in function and that function is being called rather than returning your column value.
If you want the column value then you need to prefix the column name with the table name/alias and use fine.current_date:
DECLARE
roll_counter number:=1;
initial_date date;
final_date date;
date_calc number;
BEGIN
FOR roll_counter IN 1 .. 4 LOOP
select date_of_issue
into initial_date
from borrower
where roll_no=roll_counter;
select fine.current_date
into final_date
from fine
where roll_no=roll_counter;
date_calc:=final_date-initial_date;
dbms_output.put_line(date_calc);
END LOOP;
END;
/
Which, for your sample data, outputs:
43
35
28
19
Or you can use a single query (rather than multiple queries that are called in each loop iteration):
BEGIN
FOR r IN (
SELECT f.current_date - b.date_of_issue AS diff
FROM borrower b
FULL OUTER JOIN fine f
ON (b.roll_no = f.roll_no)
WHERE COALESCE(b.roll_no, f.roll_no) BETWEEN 1 AND 4
ORDER BY COALESCE(b.roll_no, f.roll_no)
) LOOP
dbms_output.put_line(r.diff);
END LOOP;
END;
/
db<>fiddle here

Creating an Oracle trigger to stop people enrolling twice

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;

Creating a Trigger that runs on two tables

I have two tables, COPY and BORROW. Their columns are:
COPY (
Copy_id,
Bk_id,
Loc_id,
Opinion
)
and
BORROW (
Cus_evo,
B_Date,
R_Date,
Fee,
Copy_id,
Cus_id
)
I want to configure a trigger that ensures Copies that stored in a Exact location (London) (from Loc-id column of COPY table) cannot be Borrowed in December (from B_Date column of BORROW table).
I have created the following trigger:
CREATE OR REPLACE TRIGGER BORROW_TRIGGER
BEFORE INSERT ON BORROW FOR EACH ROW BEGIN
IF(TO_CHAR(TO_DATE(:NEW.B_Date, 'DD-MMM-YYYY'),'MMM'= 'DEC')
AND :NEW.Loc_id='LC0001')
THEN RAISE_APPLICATION_ERROR(-20669,'CANNOT BORROW BOOKS FROM LONDON STORE DURING MONTH DECEMBER');
END IF;
END;
/
The trigger is not created and have errors please cloud give me correct trigger for this??
Error i have experiencing
Errors: TRIGGER BORROW_TRIGGER
Line/Col: 3/5 PLS-00049: bad bind variable 'NEW.LOC_ID'
Since you only need to perform the check when b_date is in December, it's more efficient to add this as a when condition at the top of the trigger. This also simplifies the trigger logic.
create or replace trigger borrow_check_trg
before insert on borrow
for each row
when (to_char(new.b_date,'MM') = '12')
declare
l_loc_id copy.loc_id%type;
begin
select c.loc_id into l_loc_id
from copy c
where c.copy_id = :new.copy_id;
if l_loc_id = 'LC0001' then
raise_application_error(-20669, 'Books cannot be borrowed from the London store during December');
end if;
end;
You need to query the COPY table to get the field you need:
CREATE OR REPLACE TRIGGER BORROW_BI
BEFORE INSERT ON BORROW
FOR EACH ROW
DECLARE
strLoc_id COPY.LOC_ID%TYPE;
BEGIN
SELECT LOC_ID
INTO strLoc_id
FROM DUAL
LEFT OUTER JOIN COPY c
ON c.COPY_ID = :NEW.COPY_ID;
IF TO_CHAR(TO_DATE(:NEW.B_Date, 'DD-MMM-YYYY'), 'MMM') = 'DEC' AND
strLoc_id = 'LC0001'
THEN
RAISE_APPLICATION_ERROR(-20669,'CANNOT BORROW BOOKS FROM LONDON STORE DURING MONTH DECEMBER');
END IF;
END BORROW_BI;
You can check out the existence by using a Select statement with COUNT aggregation from the other table(copy) through use of common column (copy_id) among tables such as
CREATE OR REPLACE TRIGGER Trg_Borrow_Trigger_BI
BEFORE INSERT ON borrow
FOR EACH ROW
DECLARE
v_exists INT;
BEGIN
SELECT COUNT(*)
INTO v_exists
FROM copy
WHERE copy_id = :NEW.copy_id
AND loc_id = 'LC0001'
AND TO_CHAR( :NEW.b_Date, 'MM' ) = '12';
IF v_exists > 0 THEN
RAISE_APPLICATION_ERROR(-20669,
'CANNOT BORROW BOOKS FROM LONDON STORE DURING MONTH DECEMBER');
END IF;
END;
/
where
TO_DATE() conversion is superfluous
to prepend loc_id with :NEW is not possible, since the trigger is created for
the table borrow has not this column, while copy table has.

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;

Oracle SQL Creating Trigger to Increment a Sequence Number For Each Row

I'm trying to create a trigger but I have learned I can not design it as in my first attempt, which I'm showing below. This will cause a 'mutating table' error due to selecting from the table as it is being modified. It actually didn't cause this error when inserting only one record at a time, but when I insert multiple records at once it does.
The purpose of the trigger is to count the number of records in the table where the customer is equal to the customer about to be inserted, and to set the new order_num value as count+1. I also have a public key value set by the trigger which draws from a sequence. This part works ok once I remove the order_num part of the trigger and the associated SELECT. How can I achieve what I am trying to do here? Thanks in advance.
CREATE OR REPLACE TRIGGER t_trg
BEFORE INSERT ON t
FOR EACH ROW
DECLARE
rec_count NUMBER(2,0);
BEGIN
SELECT COUNT(*) INTO rec_count
FROM t
WHERE customer_id = :NEW.customer_id;
:NEW.order_num:= rec_count+1;
:NEW.order_pk_id:= table_seq.NEXTVAL;
END;
Two triggers and temp table approach can provide solution to you seek, preventing mutating table error. However performance will most likely suffer.
create global temporary table cust_temp(customer_id number, cust_cnt number);
create or replace trigger t_trig1
before insert on t
declare
begin
insert into cust_temp select customer_id, count(*) from t group by customer_id;
end;
/
CREATE OR REPLACE TRIGGER t_trg2
BEFORE INSERT ON t
FOR EACH ROW
DECLARE
rec_count number;
BEGIN
BEGIN
SELECT cust_cnt INTO rec_count
FROM cust_temp
WHERE customer_id = :NEW.customer_id;
EXCEPTION when no_data_found then rec_count := 0;
END;
:NEW.order_num:= rec_count+1;
:NEW.order_pk_id:= table_seq.NEXTVAL;
update cust_temp set cust_cnt = rec_count + 1
where customer_id = :NEW.customer_id;
END;
/