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

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)

Related

Creating trigger to allow INSERT if existing data is not null

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)
)
)

trigger to create parent element and retrieve id in postgresql

I have created the following tables:
CREATE TABLE IF NOT EXISTS public.teams (
id SERIAL PRIMARY KEY,
name VARCHAR(64) NOT NULL UNIQUE
) WITH (OIDS = FALSE);
CREATE TABLE IF NOT EXISTS public.submissions (
id SERIAL PRIMARY KEY,
team_id INTEGER REFERENCES public.teams NOT NULL,
records_num INTEGER NOT NULL,
timestamp TIMESTAMP NOT NULL
) WITH (OIDS = FALSE);
CREATE TABLE IF NOT EXISTS public.predictions (
id SERIAL PRIMARY KEY,
submission_id INTEGER REFERENCES public.submissions NOT NULL,
customer INTEGER REFERENCES public.real NOT NULL,
date DATE NOT NULL,
billing NUMERIC(20, 2) NOT NULL
) WITH (OIDS = FALSE);
CREATE TABLE IF NOT EXISTS public.real (
customer INTEGER PRIMARY KEY,
date DATE NOT NULL,
billing NUMERIC(20, 2) NOT NULL
) WITH (OIDS = FALSE);
The relation for submissions-predictions is one-to-many; users will submit predictions in packets of 1000 rows that should get the same submission id.
I am trying to create a trigger that runs BEFORE INSERT ON predictions that creates a submissions row. This is what I have so far:
CREATE OR REPLACE FUNCTION insert_submission() RETURNS TRIGGER AS
$$
BEGIN
INSERT INTO submissions(team_id, records_num, timestamp)
VALUES (1, 1, '2018-04-21 00:00:00'); /*example values, need to fill with dynamically assigned ones, specially for records_num and team_id*/
RETURN NULL;
END
$$ LANGUAGE plpgsql;
DROP TRIGGER trigger_submission ON public.predictions;
CREATE TRIGGER trigger_submission BEFORE INSERT ON predictions
EXECUTE PROCEDURE insert_submission();
So, my questions are:
How do I go about retrieving the newly created submissions.id for the row inserted by the trigger, in order to add it to all the rows inserted in predictions by the user? Do I have to run another trigger AFTER INSERT for this?
EDIT: to clarify following #bignose answer, the sequence of events would go like this:
User inserts 1000 rows into public.predictions:
INSERT INTO predictions(customer, date, billing)
VALUES
(1, '2018-01-05', 543.42),
(4, '2018-04-02', 553.21),
...
(423, '2019-11-18', 38.87) /* 1000th row */
He does not know which submission_id to insert in those rows and indeed, the submissions row for this packet of predictions doesn't exist yet so a trigger runs before to create a row in submissions that would execute something like this:
INSERT INTO public.submisssions(team_id, records_num, timestamp)
VALUES (
4, /* I will need something to retrieve team_id here */
1000, /* I will need something to count the rows of the insert that triggered this */
NOW() /* convert to timestamp */
)
This last query should return the public.submission.id value that it has just created to the insert the user requested so that it ends up being something like this:
INSERT INTO predictions(customer, date, billing)
VALUES
(#submission_id, 1, '2018-01-05', 543.42),
(#submission_id, 4, '2018-04-02', 553.21),
...
(#submission_id, 423, '2019-11-18', 38.87) /* 1000th row */
Where #submission_id should be the value retrieved from the trigger (and the some for all the 1000 rows)
How could I count the rows inserted by the user to use them as value for submissions.records_num?
How could I retrieve team.id to insert during the trigger execution, assuming I know team.name beforehand?
Thank you!
Kind regards
A trigger function, when used for a row-level trigger, has access to the old and new state of the table.
CREATE OR REPLACE FUNCTION insert_submission() RETURNS TRIGGER AS
$$
BEGIN
INSERT INTO submissions(team_id, records_num, timestamp)
VALUES (NEW.foo, NEW.bar, '2018-04-21 00:00:00');
RETURN NULL;
END
$$ LANGUAGE plpgsql;
It's not clear from the description, which fields you expect to retrieve from the row that triggers this function. So you'll need to substitute NEW.foo and NEW.bar with field references in the NEW row state.

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.

Why this sequence increments by 2?

I can't understand why this sequence is incremented by 2.
Is there any error in sequence to increment by 1? I need this to insert primary key value in table 'food'.
CREATE SEQUENCE food_id_ai START WITH 1 INCREMENT BY 1 CACHE 100;
create table food(
food_id integer,
f_name varchar(30) not null,
category varchar(30) not null,
price number(4),
amount number(4)
);
alter table food add constraint fpk primary key(food_id);
CREATE OR REPLACE TRIGGER insert_into_food
BEFORE INSERT ON food
FOR EACH ROW
BEGIN
:new.food_id:= food_id_ai.nextval;
END;
/
insert into food values(food_id_ai.nextval,'ruchi', 'chanachur' , 8, 50);
insert into food values(food_id_ai.nextval,'chips', 'chips' , 8, 50);
insert into food values(food_id_ai.nextval,'aeromatic', 'soap' , 8, 50);
insert into food values(food_id_ai.nextval,'handwash', 'toyletries', 8, 50);
insert into food values(food_id_ai.nextval,'tissue', 'toyletries' , 8, 50);
Because you're accessing the sequence both in your INSERT statement and in the trigger that is launched for each row, of course it's incremented by two.
Choose one.
I'd go for the trigger-based one, since you won't have to remember to specify the sequence in each insert statement you may execute.
In that case, you'll have to explicitly list the columns you are going to insert VALUES to:
INSERT INTO food (f_name, category, price, amount)
VALUES ('ruchi', 'chanachur' , 8, 50);
you have two options to correct this.
modify insert statement to be like this:
insert into food (f_name, category,price , amount)
values ('ruchi', 'chanachur' , 8, 50);
or modify you triggers as follow:
CREATE OR REPLACE TRIGGER insert_into_food
BEFORE INSERT ON food
FOR EACH ROW
BEGIN
if :new.food_id is null then
:new.food_id:= food_id_ai.nextval;
end if;
END;
/

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