COMP9311 Database Systems Lab6

练习使用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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 忙,忙,忙,最近的状态就是忙,最近的工作就是忙。一生的修行不是为了忙,但是忙碌充实却为一生的修行铺垫基石。不...
    彦彦归来兮阅读 3,332评论 0 0
  • “只要你过得比我好,过得比我好……” 一首老歌,在除夕夜21:59分收到的朋友的“酷狗K歌”。然而,当时我并没有听...
    xuehuikuaile阅读 3,994评论 1 1
  • 旭旭是新来的三岁男孩,耳闻他不太会表达自己,不会与他人相处,喜欢打滚、大叫。但前几天我都没有直接接触,只偶尔看到他...
    雾嘉花阅读 3,677评论 0 1