练习使用plpgsql建立trigger规则。
使用的材料与lab5一致:
ER Design
schema文件如下:
-- Beer rating database
create table Location (
id serial primary key,
state varchar(50), -- not every country has states
country varchar(50) not null
);
create table Taster (
id serial primary key,
family varchar(30), -- some people have only one name
given varchar(30) not null,
livesIn integer not null references Location(id)
);
create table Brewer (
id serial primary key,
name varchar(50) not null,
locatedIn integer not null references Location(id)
);
create table BeerStyle (
id serial primary key,
name varchar(30) not null
);
create table Beer (
id serial primary key,
name varchar(50) not null,
style integer not null references BeerStyle(id),
brewer integer not null references Brewer(id),
totRating integer default 0,
nRatings integer default 0,
rating float
);
create table Ratings (
taster integer not null references Taster(id),
beer integer not null references Beer(id),
score integer not null
constraint validRating
check (score >= 1 and score <= 5)
);
What is required is a series of semi-formal assertions:
for every Beer b (b.totRating = (sum(score) from Ratings where beer = b.id))
for every Beer b (b.nRatings = (count(score) from Ratings where beer = b.id))
for every Beer b (b.rating = b.totRating/b.nRatings, if b.nRatings > 0, null otherwise)
Write triggers and their associated PLpgSQL functions to maintain these assertions on the Beer table in response to all possible changes to the Ratings table.
Q1
insertion trigger
CREATE OR REPLACE FUNCTION insertRating()
returns trigger
AS $$
DECLARE
b Beer;
BEGIN
SELECT * into b FROM Beer WHERE id = New.beer;
b.nRatings := b.nRatings + 1;
b.totRating := b.totRating + new.score;
b.rating = b.totRating / b.nRatings;
UPDATE Beer
SET nRatings = b.nRatings,
totRating = b.totRating,
rating = b.rating
WHERE id = New.beer;
return new;
END;
$$ LANGUAGE plpgsql;
--新增的beer数据会使得评价数加1,总评分提高,该啤酒的评分重新计算
CREATE TRIGGER InsertRating after inser on Ratings
for each row execute procedure insertRating();
Q2
update trigger
CREATE OR REPLACE FUNCTION updateRating()
returns trigger
AS $$
DECLARE
nb Beer;
ob Beer;
BEGIN
SELECT * INTO nb FROM Beer WHERE id = New.beer;
--因为要更新,所以一定有new,存放在nb中
if (new.beer = old.beer) then
--如果更新的beer和过去的beer名字一致,则减去过去的评分加上现在的评分,重新计算rating即可
if(new.rating = old.rating) then
null;
else
nb.totRating := nb.totRating + new.score - old.score;
nb.rating := nb.totRating / nb.ratings;
else
--如果更新的beer和过去的beer名字不一致,那么过去的beer和新的beer数据都需要调整
SELECT * INTO ob FROM Beer WHERE id = old.beer;
ob.totRating := ob.totRating - old.score;
ob.nRatings := ob.nRatings - 1;
ob.rating := ob.totRating / ob.nRatings;
nb.totRating := nb.totRating + new.score;
nb.nRatings := nb.nRatings + 1;
nb.rating := nb.totRating / nb.nRatings;
UPDATE Beer
SET nRatings = ob.nRatings,
totRating = ob.totRating,
rating = ob.rating
WHERE id = old.beer;
END if;
UPDATE Beer
SET nRatings = nb.nRatings,
totRating = nb.totRating,
rating = nb.rating
WHERE id = new.beer;
return new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER UpdateRating after update on Ratings
for each row execute procedure updateRating();
Q3
delete trigger
CREATE OR REPLACE FUNCTION deleteRating()
returns trigger
AS $$
DECLARE
b Beer;
BEGIN
SELECT * INTO b FROM Beer WHERE id = old.beer;
b.nRatings := b.nRatings - 1;
b.totRating := b.totRating - old.score;
if (b.nRatings = 0) then
b.rating := null;
else
b.rating := b.totRating / b.nRatings;
end if;
UPDATE Beer
SET nRatings = b.nRatings,
totRating = b.totRating,
rating = b.rating
WHERE id = old.beer;
return old;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER DeleteRating before delete on Ratings
for each row execute procedure deleteRating();
网友评论