How to create date trigger? - sql

How can I create a trigger to increase a date data from my table with each next row? I had an attempt, is below the table
What I want to do is to increase date from training_date_end by 1 week. But I don't know how to do it, just studying. Can anyone help?
CREATE TABLE training
(
coach_id int NOT NULL,
customer_id int NOT NULL,
training_date_start date NULL,
training_date_end date NULL,
training_place_id int NOT NULL,
CONSTRAINT training_pk PRIMARY KEY (training_place_id)
);
create or replace trigger lucky_week
before insert or update on training
for each row
begin
update training
set training_date_end = :new.training_date_end + 7
where training_date_end = :new.training_date_end;
end;

Most probably like this:
create or replace trigger lucky_week
before insert or update on training
for each row
begin
:new.training_date_end := :new.training_date_end + 7;
end;
Because, your trigger will suffer from the mutating table error (if you insert more than a single row), and - won't do anything in that case (because row you'd like to update doesn't exist yet).

Related

PostgreSQL Trigger takes 5 seconds

i have a MediaStore Database on Postgres where tried to make a trigger which Updates the average Rating of a Product if a new review is inserted.
The Problem is: If I insert a review now, it takes more than 5 seconds.
Im not really into Databases so i thought of asking you people here :)
The DDL of the two relevant tables are:
create table review
(
review_id bigint generated by default as identity primary key,
rating integer not null CHECK (rating BETWEEN 1 AND 5),
helpful integer not null CHECK (helpful >= 0),
reviewDate date,
benutzer varchar(255),
summary varchar(255),
comment text,
produkt_id bigint NOT NULL references produkt ON DELETE CASCADE
);
create table produkt
(
produkt_id bigint generated by default as identity primary key,
asin varchar(255) unique NOT NULL,
titel varchar(1000) NOT NULL,
rating double precision,
bild varchar(1000),
verkaufsrang integer
);
And the Trigger:
CREATE OR REPLACE FUNCTION update_rating()
RETURNS TRIGGER AS $$
BEGIN
UPDATE produkt
SET rating =
(SELECT AVG(rating) AS rating
FROM review
GROUP BY produkt_id
Having review.produkt_id = new.produkt_id)
WHERE produkt_id = new.produkt_id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_rating
AFTER INSERT ON review
FOR EACH ROW
EXECUTE PROCEDURE update_rating();
Does somebody have a solution which reduces the Time of the Insert?
You don't describe your indexes. An index on review (produkt_id, rating) could help a lot if you don't have one already. If you had columns in produkt for sum and count, then you could just compute the new average without needing to traverse the entire set in review for that produkt_id. You might have a problem with concurrency, but that could be a problem with your current one too.

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.

DB2SQL Trying to create a trigger with function (Calculate days)

So for now, I created 2 tables, Booking and BookingDetails, I want to create a trigger when I key in the details of BookingDetails it will automatically update totaldays inside the Booking table. Below is my code:
BookingDetails:
create table BookingDetail (
BD_ID int primary key not null,
Date_In date,
Date_Out date,
BK_ID int,
Room_ID int,
foreign key(BK_ID) references Booking(BK_ID),
foreign key(Room_ID) references Room(Room_ID)
)
And also Booking
create table Booking (
BK_ID int primary key not null,
BK_Date Date,
BK_TotalDays int,
BK_PayStatus char(6),
Cus_ID int,
Emp_ID int,
foreign key(Cus_ID) references customer(Cus_ID),
foreign key(Emp_ID) references Employee(Emp_ID)
)
With the function and trigger created:
create function countdays(t1 date, t2 date)
returns INT
return (timestampdiff(16, char(timestamp(t2) - timestamp(t1))))
create trigger totaldays
after insert on bookingdetail
referencing new as n
for each row mode db2sql
update booking
set bk_totaldays =
countdays((select date_in from bookingdetail), (select date_out from
bookingdetail))
where booking.bk_id = n.bk_id;
I have no problem executing these syntax, but when I try to input a new record inside Booking Detail to let the trigger triggers in Booking, errors occured, may I ask why? Thanks in advance.
Look at the information provided by the SQL error:
db2 ? SQL0811
SQL0811N The result of a scalar fullselect, SELECT INTO statement, or
VALUES INTO statement is more than one row.
So this part of your trigger expressions returns more than 1 row
set bk_totaldays = countdays((select date_in from bookingdetail),
(select date_out from bookingdetail))
Fix this to return a single row.
#MichaleTiefenbacher is correct about the cause of the error, but he's wrong about what to do about it. Let's take another look at your trigger again:
create trigger totaldays
after insert on bookingdetail
referencing new as n -- wait, what's this?
for each row mode db2sql
update booking
set bk_totaldays =
countdays((select date_in from bookingdetail), (select date_out from bookingdetail))
where booking.bk_id = n.bk_id;
You have a reference to the value you just inserted! When using FOR EACH ROW, the table reference NEW refers to the singular row just inserted. So you don't even need to look at the full table, just use what you just worked with:
create trigger totaldays
after insert on bookingdetail
referencing new as n
for each row mode db2sql
update booking
set bk_totaldays = countdays(n.date_in, n.date_out)
where booking.bk_id = n.bk_id;
(As I mentioned in my comment to your question, you'll likely want to change how you calculate the days, but that's irrelevant for this)

Trigger for generating IDs

I would like to create trigger for generating ID in table:
CREATE TABLE client (
clientID INT PRIMARY KEY NOT NULL,
name VARCHAR(16) NOT NULL,
surname VARCHAR(16) NOT NULL,
personalID VARCHAR(10) NOT NULL,
CONSTRAINT verifyPersonalID CHECK ((personalID BETWEEN 1000000000 and 9999999999) and (MOD(personalID, 11) = 0))
);
I tried to write it like this but it keeps returning errors and I dont know why. Can you, please, give me an advice what Im doing wrong?
CREATE OR REPLACE TRIGGER clientID
AFTER INSERT
ON client
FOR EACH ROW
BEGIN
UPDATE client
SET client.clientID = klientSeq.nextval
WHERE :new.personalID = client.personalID;
END;
/
You want a before insert trigger:
CREATE OR REPLACE TRIGGER klientID
BEFORE INSERT
ON klient
FOR EACH ROW
BEGIN
SELECT klientSeq.nextval INTO :new.cisloKlienta FROM dual;
END;
Maybe instead of creating a trigger, you could make the default value of the primary key the next value of the sequence.

Oracle Script problem - create trigger not terminating

I am trying to make some changes to an oracle database and have a script put together to do so. The problem is when it gets to a point in the script where I am creating a trigger it seems like the Create Trigger block does not properly terminate, when I look at the trigger afterwards it contains all of the remaining code in the script.
This is what I have:
CREATE OR REPLACE TRIGGER user_publish_log_trg
BEFORE INSERT ON USER_PUBLISH_LOG
FOR EACH ROW
BEGIN
SELECT user_publish_log_seq.NEXTVAL INTO :NEW.Id FROM dual;
END user_publish_log_trg;
CREATE TABLE USER_APPROVAL_LOG
(
Id number(10) NOT NULL ,
CommodityId number(10) NOT NULL,
QuarterEndDate DATE NOT NULL,
ActionId int NOT NULL ,
...
What am I doing wrong in ending the trigger?
You need to terminate the PL/SQL by using a slash on a new line, like this:
CREATE OR REPLACE TRIGGER user_publish_log_trg
BEFORE INSERT ON USER_PUBLISH_LOG
FOR EACH ROW
BEGIN
SELECT user_publish_log_seq.NEXTVAL INTO :NEW.Id FROM dual;
END user_publish_log_trg;
/
CREATE TABLE USER_APPROVAL_LOG
(
Id number(10) NOT NULL ,
CommodityId number(10) NOT NULL,
QuarterEndDate DATE NOT NULL,
ActionId int NOT NULL ,
...