COMP9311 Database Systems Lab6

练习使用plpgsql建立trigger规则。
使用的材料与lab5一致:


COMP9311 Database Systems Lab6_第1张图片
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();

你可能感兴趣的:(COMP9311 Database Systems Lab6)