Creating trigger to allow INSERT if existing data is not null - sql

I have the following table and I want to create a trigger that allows the insertion of values if the value of 'ActualLeaveDate' is not null:
CREATE TABLE Bed
(
PatBedNum NUMBER GENERATED BY DEFAULT AS IDENTITY START WITH 1 INCREMENT BY 1,
WardNum NUMBER(4),
BedNum NUMBER(2),
PatNum VARCHAR2(6) CHECK (PatNum LIKE '%P%'),
DateOnWaitlist DATE,
ExpectedStayDuration VARCHAR2(8) CHECK (ExpectedStayDuration LIKE '% days%'),
DatePlacedInWard DATE,
ExpectedLeaveDate DATE,
ActualLeaveDate DATE,
CONSTRAINT BedPK PRIMARY KEY (PatBedNum),
CONSTRAINT PatBedNum FOREIGN KEY (PatNum)
REFERENCES Patient (PatNum) ON DELETE CASCADE,
CONSTRAINT PatWardNum FOREIGN KEY (WardNum)
REFERENCES Ward (WardNum) ON DELETE CASCADE
);
I tried to create a trigger using the following script:
CREATE TRIGGER BedCheck2
BEFORE INSERT ON BED
FOR EACH ROW
BEGIN
IF (:Old.ActualLeaveDate IS NOT NULL)
THEN
INSERT INTO Bed (PatBedNum, WardNum, BedNum, PatNum, DateOnWaitlist, ExpectedStayDuration, DatePlacedInWard, ExpectedLeaveDate, ActualLeaveDate)
VALUES (:New.PatBedNum, :New.WardNum, :New.BedNum, :New.PatNum, :New.DateOnWaitlist, :New.ExpectedStayDuration, :New.DatePlacedInWard, :New.ExpectedLeaveDate, :New.ActualLeaveDate);
ELSE
RAISE_APPLICATION_ERROR (-20002, 'Bed ' || :Old.BedNum || 'is not empty!');
END IF;
END;
/
However, when I enter the following insert command, it skips the insertion portion of the trigger:
INSERT INTO BED VALUES (5, 11, 84, 'P10787', '12/1/20', '5 days', '17/1/20', '22/1/20', NULL);
Additionally, it straights directly to the RAISE_APPLICATION_ERROR even though the data in 'ActualLeaveDate' in the database IS NOT NULL.
Am I missing something here or doing something wrong?

Why not simply add a NOT NULL constraint to that column?
CREATE TABLE Bed (
...,
ActualLeaveDate DATE NOT NULL,
...
)
If you want to prevent both ActualLeaveDate and BedNum to be null (or not null), you can use a check constraint:
CREATE TABLE Bed (
...,
BedNum NUMBER(2),
ActualLeaveDate DATE,
...
CHECK (
(BedNum IS NOT NULL AND ActualLeaveDate IS NULL)
OR (BedNum IS NULL AND ActualLeaveDate IS NOT NULL)
)
)

Related

It is possible to call trigger with old, or new in procedure?

I want to insert data using procedure. At first I wanna insert match, but id for football match is generated automatically using trigger. Secondly I want use this id for other table - stats_footballmatch_club. It is possible?
INSERT INTO FootballMatch(FootballMatch_Date, Stadium_Id, Tournament_Id) VALUES(TO_DATE('02-04-22','DD-MM-YY'), 1, 1);
INSERT INTO STATS_FOOTBALLMATCH_CLUB(FootballMatch_Id, Club_Id , Goals, Shots, BallControl, Pass) VALUES (1, 1, 0, 0, 0, 0);
INSERT INTO STATS_FOOTBALLMATCH_CLUB(FootballMatch_Id, Club_Id , Goals, Shots, BallControl, Pass) VALUES (1, 2, 0, 0, 0, 0);
If for example, my trigger name is FOOTBALLMATCHTRIGGER, can I do something like that?
INSERT INTO STATS_FOOTBALLMATCH_CLUB(FootballMatch_Id, Club_Id , Goals, Shots, BallControl, Pass) VALUES (FOOTBALLMATCHTRIGGER:new, 2, 0, 0, 0, 0);
This is the footballmatchtrigger:
CREATE OR REPLACE TRIGGER FootballMatchTrigger
BEFORE INSERT
ON FootballMatch
FOR EACH ROW
BEGIN
if :NEW.FootballMatch_Id is null then
SELECT FootballMatchIdSequence.NEXTVAL
INTO :new.FootballMatch_Id
FROM dual;
end if;
END;
And there is footballmatch table:
CREATE TABLE FootballMatch(
FootballMatch_Id NUMBER(4) PRIMARY KEY,
FootballMatch_Date DATE,
Stadium_Id NUMBER(4) REFERENCES Stadium(Stadium_Id) on delete cascade,
Tournament_Id NUMBER(4) REFERENCES Tournament(Tournament_Id) on delete cascade
);
It also refers to other tables like stadium, and tournament.
Short answer: no you cannot use a trigger for that. A trigger cannot be referenced in code.
Solution: Use the RETURNING INTO clause.
Simplified example:
DROP TABLE stats_footballmatch_club;
DROP TABLE footballmatch;
DROP SEQUENCE footballmatch_s;
CREATE TABLE footballmatch(
footballmatch_id NUMBER(4) PRIMARY KEY,
footballmatch_date DATE
);
CREATE SEQUENCE footballmatch_s;
CREATE OR REPLACE TRIGGER footballmatch_bi BEFORE
INSERT ON footballmatch
FOR EACH ROW
BEGIN
:new.footballmatch_id := footballmatch_s.nextval;
END footballmatch_bi;
/
CREATE TABLE stats_footballmatch_club(
stats_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
goals NUMBER,
club VARCHAR2(100),
footballmatch_id NUMBER(4) REFERENCES footballmatch(footballmatch_id) on delete cascade
);
DECLARE
l_footballmatch_id footballmatch.footballmatch_id%TYPE;
BEGIN
INSERT INTO footballmatch(footballmatch_date) VALUES(TO_DATE('02-04-22','DD-MM-YY'))
RETURNING footballmatch_id INTO l_footballmatch_id;
INSERT INTO stats_footballmatch_club (footballmatch_id, goals, club) VALUES (l_footballmatch_id,3, 'Real Madrid');
INSERT INTO stats_footballmatch_club (footballmatch_id, goals, club) VALUES (l_footballmatch_id,2, 'PSG');
END;
/
Notes:
No need for SELECT INTO to assign sequence.NEXTVAL. Just assign using := sequence.NEXTVAL
Don't even need the trigger for primary key - identity columns are a lot handier ( see table stats_footballmatch_club primary key)

Oracle adding records from multiple tables

My objective is to build a master Serial No table from many different tables and each Serial No must be unique.
CREATE TABLE "TBL_SERIAL_NUMBER_MASTER"
( "INTERNAL_RECORD_ID" VARCHAR2(60) NOT NULL ENABLE,
"ASSET_ID" VARCHAR2(60),
"SERIAL_NUMBER" VARCHAR2(1000) NOT NULL ENABLE,
"VALID" VARCHAR2(60) DEFAULT 'Valid',
"HOST_NAME" VARCHAR2(255),
CHECK ( valid IN ('Invalid', 'Valid')) ENABLE,
CHECK ( valid IN ('Invalid', 'Valid')) ENABLE,
CHECK ( valid IN ('Invalid', 'Valid')) ENABLE,
CONSTRAINT "TBL_SERIAL_NUMBER_MASTER_CON" PRIMARY KEY ("SERIAL_NUMBER") ENABLE
) ;
CREATE OR REPLACE TRIGGER "TBL_SERIAL_NUMBER_MASTER_INTER" BEFORE
INSERT
ON tbl_serial_number_master FOR EACH ROW WHEN (
NEW.internal_record_id IS NULL
) BEGIN :NEW.internal_record_id := tbl_serial_number_master_inter.NEXTVAL;
END;
/
ALTER TRIGGER "TBL_SERIAL_NUMBER_MASTER_INTER" ENABLE;
I have already updated the SERIAL_NUMBER field using one table. Now I am trying to add more Serial Nos from another table. These other tables have duplicate and null value Serial Nos. Can you please advise how I can create the query to add unique Serial Nos that are already not in the TBL_SERIAL_NUMBER_MASTER.
Please assume following fields for the other table.
TABLE_SOURCE_B
ID :-PK
SERIAL_NUMBER
List item
Name
In your Master Table, 'Internal_Record_Id' has Not Null constraint. So I am inserting values of ID field of Source table into 'internal_record_id'.
Try the following query.
INSERT INTO
TBL_SERIAL_NUMBER_MASTER (INTERNAL_RECORD_ID,SERIAL_NUMBER)
SELECT ID,SERIAL_NUMBER
FROM TABLE_SOURCE_B B
WHERE B.SERIAL_NUMBER NOT IN(SELECT SERIAL_NUMBER
FROM TBL_SERIAL_NUMBER_MASTER);
Hope it helps!

Trigger in Oracle

I create the following tables:
create table Tasks(
code varchar2(9),
name varchar2(40),
start_date date,
end_date date,
constraint pk_code primary key (code)
);
create table secondary_Tasks(
code varchar2(9),
code_primary varchar2(9),
name varchar2(40),
start_date date,
end_date date,
constraint pk_code2 primary key (code),
constraint fk_code foreign key (code_primary) references Tasks (code)
);
I inserted the following datas:
insert into Tasks values ('001','Task1',to_date('2010-02-15','YYYY-MM-DD'),to_date('2011-04-12','YYYY-MM-DD'));
insert into Tasks values ('002','Task2',to_date('2015-08-11','YYYY-MM-DD'),to_date('2015-09-25','YYYY-MM-DD'));
insert into Tasks values ('003','Task3',to_date('2016-05-09','YYYY-MM-DD'),null);
insert into Tasks values ('004','Task4',to_date('2014-01-23','YYYY-MM-DD'),to_date('2014-06-04','YYYY-MM-DD'));
insert into secondary_Tasks values ('s01','001','Secundary_Task1',to_date('2010-03-16','YYYY-MM-DD'),to_date('2011-05-13','YYYY-MM-DD'));
insert into secondary_Tasks values ('s02','002','Secundary_Task2',to_date('2015-09-12','YYYY-MM-DD'),to_date('2015-10-26','YYYY-MM-DD'));
insert into secondary_Tasks values ('s04','004','Secundary_Task4',to_date('2014-02-24','YYYY-MM-DD'),to_date('2014-07-05','YYYY-MM-DD'));
The question is how to make a trigger that does not allow me to add a secondary task to task '003' because It hasnt finished.
I'm unable to test right now, but I'd try something like
create or replace trigger secondary_tasks_bi
before insert or update on secondary_tasks for each row
declare
v_dummy varchar2(1);
begin
select null
into v_dummy
from tasks
where code = :new.code_primary
and end_date is null;
raise_application_error(-20001, 'Can''t add a secondary task to task ' || :new.code_primary || ' because it hasn''t finished');
exception
when no_data_found then
null;
end;
/
Note that this assumes that code_primary is never null: you probably want to add a NOT NULL clause to it.

Basic primary key / foreign key with constraint, sequence, trigger

Learner here in Oracle 11g. I'm having an issue with INSERTing some rows into two tables that are linked by a primary/foreign key relationship.
Basically I create a sequence to start with 1000 and increment by 1.
Then create a 'STORE' table with a ST_ID column
The ST_ID column is linked to the SEQUENCE with a TRIGGER.
Then I have an 'EMPLOYEE' table that has a EST_ID field that is a foreign key to the ST_ID column in the STORE table.
However, when I tried to insert rows I initially got a error saying EST_ID could not be null. So I created a sequence and trigger for EST_ID and now I'm getting an error saying the foreign key constraint is being violated.
I think that was maybe the wrong thing to do. Do I really want E_ID and EST_ID to be identical and how would I get that to happen? With some kind of trigger?
The actual code:
CREATE SEQUENCE "STORSEQ" MINVALUE 1000 MAXVALUE 9999 INCREMENT BY 1 START WITH 1000 NOCACHE NOORDER
NOCYCLE ;
CREATE TABLE "STORE"
( "ST_ID" CHAR(4) NOT NULL ENABLE,
"STADDR_ID" CHAR(4) NOT NULL ENABLE,
CONSTRAINT "STORE_PK" PRIMARY KEY ("ST_ID") ENABLE
) ;
CREATE TABLE "EMPLOYEE"
( "E_ID" CHAR(8) NOT NULL ENABLE,
"EF_NAME" VARCHAR2(20) NOT NULL ENABLE,
"EL_NAME" VARCHAR2(20) NOT NULL ENABLE,
"EST_ID" CHAR(4) NOT NULL ENABLE,
CONSTRAINT "EMPLOYEE_PK" PRIMARY KEY ("E_ID") ENABLE
) ;
alter table "EMPLOYEE" add CONSTRAINT "EMPLOYEE_CON" foreign key ("EST_ID") references
"STORE" ("ST_ID")
/
CREATE OR REPLACE TRIGGER "BI_STORE"
before insert on "STORE"
for each row
begin
if :NEW."ST_ID" is null then
select "STORSEQ".nextval into :NEW."ST_ID" from dual;
end if;
end;
/
At the moment my INSERT code looks like this:
INSERT INTO STORE
(ST_ID, STADDR_ID)
VALUES
(DEFAULT, DEFAULT);
INSERT INTO EMPLOYEE
(EF_NAME, EL_NAME)
VALUES
('James', 'Smith');
When you try to insert data into table that has foreign key reference, it will not get value for id automatically, you need to pass that value.
You can do this:
declare
v_store_id integer;
begin
INSERT INTO STORE (ST_ID, STADDR_ID) VALUES (DEFAULT, DEFAULT)
RETURNING ST_ID INTO v_Store_id;
INSERT INTO EMPLOYEE (EF_NAME, EL_NAME, EST_ID)
VALUES ('James', 'Smith', v_store_id);
end;
You can also insert id in store id table without trigger using this
declare
v_store_id integer;
begin
INSERT INTO STORE (ST_ID, STADDR_ID) VALUES ("STORSEQ".nextval, DEFAULT)
RETURNING ST_ID INTO v_Store_id;
INSERT INTO EMPLOYEE (EF_NAME, EL_NAME, EST_ID)
VALUES ('James', 'Smith', v_store_id);
end

Trigger with current date query/where clause

we want to create a trigger which checks if a new measurement (=messung) point lies within the current glacier shape (=umriss).
are tables look like this:
glacier shape (=Umriss)
create table umriss
(
umr_nr number (4) not null,
umr_datum date,
GLST_ID number (4) not null,
shape mdsys.sdo_geometry,
GLETSCHER_ID number (3) not null
)
;
alter table umriss
add constraint umriss_glst_pk
primary key (umr_nr, GLST_ID, GLETSCHER_ID)
;
ALTER TABLE umriss
ADD CONSTRAINT umriss_gletscherstand_fk
FOREIGN KEY (GLST_ID, GLETSCHER_ID)
REFERENCES GLETSCHERSTAND(GLST_ID, GLETSCHER_ID);
new measurement (=Messung)
CREATE TABLE MESSUNG
(
MESS_NR number (4) not null,
MESS_DAT date,
MESS_AKK number (20) NOT NULL,
MESS_SCHMELZ number (20) NOT NULL,
SHAPE mdsys.sdo_geometry,
MESS_BILD blob,
KMPGN_NR NUMBER (4) NOT NULL
);
ALTER TABLE MESSUNG
ADD CONSTRAINT messung_pk
PRIMARY KEY (MESS_NR);
ALTER TABLE MESSUNG
ADD CONSTRAINT messung_messkampagne_fk
FOREIGN KEY (KMPGN_NR)
REFERENCES MESSKAMPAGNE(KMPGN_NR);
Trigger
CREATE OR REPLACE
TRIGGER MESSUNG_in_UMRISS_TRI
BEFORE INSERT OR UPDATE ON MESSUNG
FOR EACH ROW
DECLARE
num_check NUMBER;
BEGIN
SELECT COUNT (*) INTO num_check
FROM UMRISS u
WHERE mdsys.sdo_contains (u.shape, :NEW.point) = 'TRUE';
IF num_check <> 1
THEN
RAISE_APPLICATION_ERROR (=20500, 'Messung in keinem Umriss')
END IF;
END;
How do we iplement the function so the trigger only checks within the most curretn glacier shape?
Thanks for your help!
This will return the one row fron UMRISS which matches the most recent date in the table.
SELECT COUNT (*) INTO num_check
FROM UMRISS u
WHERE mdsys.sdo_contains (u.shape, :NEW.point) = 'TRUE'
AND u.umr_datum = ( select max(d.;umr_datum) from UMRISS d );
This sort of query is the price for keeping historical data in teh same table as the current record.
it seems that POINT ist not defined in your example?
However, you might consider a check constraint because a trigger would only be enforced when a MESSUNG record is inserted or updated, but not when UMRISS is changed
alter table MESSUNG add constraint MESSUNG_CC_CONTAINS check(mdsys.sdo_contains (shape, point) = 'TRUE')
In case you're frequently updating the UMRISS records it might not be feasable at all to enforce such a constraint because Oracle would have to check all MESSUNG records when UMRISS is updated or deleted. Perhaps consider an additional mapping table between MESSUNG and UMRISS that you can update separately.