I have two tables:
Assessment and Assessment_Announcement
CREATE TABLE "ASSESSMENT"
( "ASSESSMENT_NAME" VARCHAR2(50) NOT NULL ENABLE,
"DEADLINE_DATE" DATE NOT NULL ENABLE,
CONSTRAINT "ASSESSMENT_PK" PRIMARY KEY ("ASSESSMENT_NAME") ENABLE
)
CREATE TABLE "ASSESSMENT_ANNOUNCEMENT"
( "ASSESSMENT_NAME" VARCHAR2(50) NOT NULL ENABLE,
"DEADLINE_DATE" DATE NOT NULL ENABLE,
"ATTENTION" VARCHAR2(500) NOT NULL ENABLE,
CONSTRAINT "ASSESSMENT_ANNOUNCEMENT_PK" PRIMARY KEY ("ASSESSMENT_NAME") ENABLE
)
I am looking at implementing a trigger which updates to the 'ASSESSMENT_ANNOUNCEMENT' table when the date in the DEADLINE_DATE column on the ASSESSMENT table is within 7 days. The data might have been stored for 14 days, but when it is within 7 days of the deadline - it triggers. This shouldn't trigger on insert unless the insert date is within 7 days of the deadline.
So far I have the following code:
CREATE OR REPLACE TRIGGER "TEST"
AFTER INSERT ON ASSESSMENT
FOR EACH ROW
BEGIN
insert into ASSESSMENT_ANNOUNCEMENT(ASSESSMENT_NAME, DEADLINE_DATE, ATTENTION)
values (:new.ASSESSMENT_NAME, :new.DEADLINE_DATE, 'DEADLINE IS 7 DAYS OR LESS');
WHERE DEADLINE_DATE >= (SYSDATE) - 7
Any help and guidance would be greatly appreciated!
Thanks!
It sounds like you want
BEGIN
IF :new.deadline_date >= sysdate + 7
THEN
INSERT INTO assessment_announcement( assessment_name, deadline_date, attention )
VALUES( :new.assessment_name, :new.deadline_date, 'Deadline is 7 days or less' );
END IF;
END;
Note that an Oracle DATE always contains a day component and a time component. So sysdate + 7 returns exactly 24*7 = 168 hours ago. If it is currently 4:00 PM on Feb 26, it would return 4:00 PM on March 5. If you want the trigger to insert a row in assessment_announcement if the deadline_date is any time on March 5, you'd need trunc(sysdate) + 7.
I don't believe you can accomplish what you want with a trigger as you have no guarantee that the table will be updated within the timeframe you want. Instead you should be looking to run a stored procedure to do this that can be scheduled on an appropriate interval; say every day or every 12 hours. I've done this with MSSQL and not oracle, but this article should help you get started on your way.
The PL-SQL BLock you want to schedule is your query above. You could modify it to be
insert into ASSESSMENT_ANNOUNCEMENT(ASSESSMENT_NAME, DEADLINE_DATE, ATTENTION)
Select ASSESSMENT_NAME, DEADLINE_DATE, 'Deadline is 7 days or less'
FROM ASSESSMENT
WHERE DEADLINE_DATE >= (SYSDATE) - 7
Related
I have the following schema:
CREATE TABLE EPOCA
(
ID INT
CONSTRAINT PK_EPOCA PRIMARY KEY,
NOME VARCHAR(250),
DATA_INI DATE
CONSTRAINT NN_EPOCA_DATA_INI NOT NULL,
DATA_FIM DATE,
CONSTRAINT CK_EPOCA_DATAS CHECK (DATA_INI < DATA_FIM)
);
And the following trigger, that is supposed to raise an error whenever an EPOCA is inserted into the database and the period between DATA_FIM and DATA_INI is overlapped with other periods of other EPOCAS.
CREATE OR REPLACE TRIGGER TRGEPOCASNAOSOBREPOSTAS
AFTER INSERT OR UPDATE
ON EPOCA
FOR EACH ROW
BEGIN
IF INSERTING THEN
IF :OLD.DATA_INI <= :NEW.DATA_INI AND :OLD.DATA_FIM >= :NEW.DATA_FIM THEN
RAISE_APPLICATION_ERROR(-20021, 'INSERT FAILED BECAUSE SELECTED DATES OVERLAP EXISTENT ONES');
END IF;
ELSIF UPDATING THEN
IF :OLD.DATA_INI <= :NEW.DATA_INI AND :OLD.DATA_FIM >= :NEW.DATA_FIM THEN
RAISE_APPLICATION_ERROR(-20022, 'UPDATE FAILED BECAUSE SELECTED DATES OVERLAP EXISTENT ONES');
END IF;
END IF;
END;
Think of it as: if I define the summer between june 1st and 30th of august I cannot define anyother period of the year with that period of time, nor can I update an existing period with those dates or nothing between june 1st and 30th august.
Right now I can insert any EPOCA with the same date as any other present in the table and I can update the date of any EPOCA with the dates of other EPOCAS and it allows me. What can I change?
I guess you would need a trigger like this one:
CREATE OR REPLACE TRIGGER TRGEPOCASNAOSOBREPOSTAS
AFTER INSERT OR UPDATE
ON EPOCA
c INTEGER;
BEGIN
SELECT COUNT(*)
INTO c
FROM EPOCA e
WHERE EXISTS (
SELECT 1
FROM EPOCA ee
WHERE (e.DATA_INI BETWEEN ee.DATA_INI AND ee.DATA_FIM
OR e.DATA_FIM BETWEEN ee.DATA_INI AND ee.DATA_FIM)
AND ee.ROWID <> e.ROWID);
IF c > 0 THEN
RAISE_APPLICATION_ERROR(-20021, 'INSERT FAILED BECAUSE SELECTED DATES OVERLAP EXISTENT ONES');
END IF;
END;
Note, the FOR EACH ROW clause is not given!
Otherwise the trigger performs only the currently inserted/updated row but does not compare to any existing data.
Consider also cases like this:
In the table you have a period from 1st to 30th of August, then you try to add period for 1st of May to 31th of December. Of course, such situations should be also blocked by the trigger. Thus you need only a statement-level trigger, i.e. a row level trigger which checks only the inserted/updated row is not sufficient.
I am trying to add x number of days to a variable within a table by deriving another date from the same table.
For example, in my BILLING table, it has 2 dates - BillDate and DueDate.
And so, I am trying to add a trigger before the insertion, such that it will takes in the BillDate and add 30 days to derive the DueDate.
While doing so, I got a bunch of errors, as follows:
dbfiddle
CREATE TABLE BILLING
(
BillDate DATE NOT NULL,
DueDate DATE NULL
);
-- Got ORA-24344: success with compilation error
CREATE TRIGGER duedate_trigger BEFORE INSERT ON BILLING
FOR EACH ROW
begin
set DueDate = :new.DueDate: + 30
end;
-- Got ORA-04098: trigger 'FIDDLE_FBHUOBXMWRPYBBXPIKTW.DUEDATE_TRIGGER' is invalid and failed re-validation
INSERT INTO BILLING
VALUES ((Date '2020-07-23'), NULL);
For the insertion, I have tried removing the NULL, but still I am getting a bunch of errors.
Any ideas?
Also, in the event, if the insertion statement also does includes in the due date too, will this affects the trigger? Trying to cater for 2 scenarios, generate a due date if not give, else if given, check if it is within 30 days from BillDate and update it... (likely I may have overthink/ overestimated that this is doable?)
CREATE OR REPLACE TRIGGER duedate_trigger BEFORE INSERT ON BILLING
FOR EACH ROW
begin
:new.DueDate := :new.BillDate + 30;
end;
INSERT INTO BILLING (BillDate ) values (sysdate);
CREATE OR REPLACE TRIGGER duedate_trigger
BEFORE INSERT ON billing
FOR EACH ROW
DECLARE
v_dueDate_derive NUMBER;
BEGIN
v_dueDate_derive = 30;
:new.DueDate = :new.BillDate + v_dueDate_derive;
END;
Days can be easily added by +, so it should not be the problem.
I believe there may be something wrong with INSERT itself.
Could you try to put INSERT like this?
INSERT INTO BILLING
VALUES (TO_DATE('2020-07-23'), NULL);
How do I check that when a user enters their date of birth into my database that they are 12 and over?
I am using SQL plus.
Right now I have somthing like this:
create table test(dateofbirth date not null,
CONSTRAINT dateofbirth_ck CHECK(TO_CHAR(dateofbirth, 'YYYY-MM-DD') <= '2002-02-09')
);
The problem with this is that it does not update every day, so the '2002-02-09' should be changed every day.
I know what to do but I'm not sure how to execute it. I want to get the 'Dateofbirth' & sysdate - 12 years and if user is above 12 it will let them.
Oracle won't you let use sysdate in a check constraint (or other functions whose return value is not constant over time).
You can, however, write a trigger that implements the same logic:
create or replace trigger trg_mytable_dateofbirth
before insert or update on mytable
for each row
begin
if :new.dateofbirth > add_months(sysdate, -12 * 12)
then
raise_application_error( -20001,
'invalid date of birth - current value:'
|| to_char(:new.dateofbirth, 'yyyy-mm-dd')
|| ', limit as of today:'
|| to_char(add_months(sysdate, -12 * 12), 'yyyy-mm-dd')
);
end if;
end;
/
Demo on DB Fiddle:
insert into mytable(dateofbirth) values (date'2000-01-01')
-- 1 rows affected
insert into mytable(dateofbirth) values (date'2010-01-01')
-- ORA-20001: invalid date of birth - current value:2010-01-01, limit as of today:2008-02-10
-- ORA-06512: at "FIDDLE_MYQIVTFXTMKGROKMNOGB.TRG_MYTABLE_DATEOFBIRTH", line 4
-- ORA-04088: error during execution of trigger 'FIDDLE_MYQIVTFXTMKGROKMNOGB.TRG_MYTABLE_DATEOFBIRTH'
I am having trouble with an SQL Trigger on a View. I have a table called Absence like so:
CREATE TABLE Absence
(
absence_id_pk varchar(6) NOT NULL,
staff_id_fk varchar(6),
start_date date,
end_date date,
reason varchar(30),
PRIMARY KEY (absence_id_pk),
FOREIGN KEY (staff_id_fk) REFERENCES Full_Time_Employee(staff_id_fk)
);
which records periods of staff absence from work.
Here is the problem! I would like to create a trigger that sends a message to the DBMS when a member of staff's total number of absent days is greater than 20 e.g. Eek! This staff is taking too much sick leave. In reality, it would probably be checked against a period of time e.g. in the last 6 months but this doesn't need to be that complex. Simply when the total of periods of absence is above 20 days on insert of a new record into the Absence table.
After reading some of the comments I have made this new trigger:
create or replace TRIGGER absence_check
BEFORE INSERT
ON absence
FOR EACH ROW
DECLARE
staffid absence.staff_id_fk%TYPE;
days number;
BEGIN
SELECT SUM(end_date - start_date) INTO days
FROM absence
WHERE staff_id_fk = staffid;
IF days > 20
THEN
DBMS_OUTPUT.PUT_LINE('Total days absent are more than 20' || staffid);
END IF;
END;
Any advice/guidance/solutions would be greatly appreciated! It would be a bonus if the message could print out the staff_id_fk that has just violated the > 20 days absent rule.
P.S. I am a University student and although this may be implemented in other ways, we have been asked to try and create triggers for our database scenario!
create or replace TRIGGER absence_check
BEFORE INSERT
ON absence
FOR EACH ROW
DECLARE
staffid absence.staff_id_fk%TYPE := :NEW.staff_id_fk;
days number;
BEGIN
SELECT SUM(end_date - start_date) INTO days
FROM Absence, Staff
WHERE Absence.staff_id_fk = staffid;
IF days > 20
THEN
DBMS_OUTPUT.PUT_LINE('Warning: Total number of days absent is more than 20 for staff member: ' || staffid);
END IF;
END;
Thanks for everyone's help and suggestions. It feels great when you finally crack a problem and I have learnt so much about Triggers by exploring this problem with your help.
I am new to triggers in Oracle. I created an EVENT table with this syntax:
CREATE TABLE Event
(event_id NUMBER (3) NOT NULL,
event_date DATE NOT NULL,
venue_id NUMBER (2) NOT NULL,
concert_id NUMBER (3) NOT NULL
);
I want to create a trigger to ensure that concerts cannot run during the month of August. I tried the following code to create the trigger. The trigger was created successfully but after inserting a date with the month of August, it was inserted. This is not suppose to be.
CREATE OR REPLACE TRIGGER check_date
BEFORE INSERT ON event
DECLARE
event_date date;
BEGIN
IF (to_char(event_date, 'mm') = 8) THEN
raise_application_error(-20000, 'Concerts cannot be run during August');
END IF;
END;
First, the trigger needs to be a row-level trigger not a statement-level trigger. You want the trigger to be fired for every row that is inserted not just once for every statement. Declaring the trigger a row-level trigger allows you to see the data for each row that is being inserted.
Second, you don't want to declare a local variable event_date. You want to look at :new.event_date which is the event_date for the row that is being inserted.
If I put those two together
CREATE OR REPLACE TRIGGER check_date
BEFORE INSERT ON event
FOR EACH ROW
BEGIN
IF (to_char(:new.event_date, 'mm') = 8) THEN
raise_application_error(-20000, 'Concerts cannot be run during August');
END IF;
END;
then you'll get the behavior you want
SQL> insert into event values( 1, date '2012-08-01', 1, 1 );
insert into event values( 1, date '2012-08-01', 1, 1 )
*
ERROR at line 1:
ORA-20000: Concerts cannot be run during August
ORA-06512: at "SCOTT.CHECK_DATE", line 3
ORA-04088: error during execution of trigger 'SCOTT.CHECK_DATE'
As a general matter of cleanliness, you also want to compare strings with strings and numbers with numbers. So you would want either
to_number( to_char(:new.event_date, 'mm') ) = 8
or
to_char(:new.event_date, 'fmmm') = '8'
or
to_char(:new.event_date, 'mm') = '08'
change:
IF (to_char(event_date, 'mm') = 8) THEN
to:
IF (to_char(event_date, 'mm') = '08') THEN
You're comparing between string and number.