触发和功能问题

triggers and functions trouble

我目前正在经历尝试学习函数和触发器的成长过程中的痛苦。我正在尝试做一本正在阅读的书中的问题,但我不明白如何做某些部分。

使用这个table

create table movies (
    id      integer primary key,
    title   varchar(255) not null,
    year    integer
);

insert into movies values (1, 'The Croods', 2013);
insert into movies values (2, 'Now You See Me', 2013);
insert into movies values (3, 'Argo', 2012);
insert into movies values (4, 'Jurassic World', 2015);

create table discs (
    id          integer primary key,
    movie_id    integer not null references movies(id),
    type_id     integer references disc_types(id),
    price       decimal(10,2),
    available   boolean
);

insert into discs values (1, 1, 1, 1.59, 't');
insert into discs values (2, 1, 1, 1.59, 'f');
insert into discs values (3, 1, 2, 2.99, 'f');
insert into discs values (4, 2, 1, 1.29, 't');
insert into discs values (5, 2, 1, 1.29, 't');
insert into discs values (6, 2, 2, 2.99, 't');
insert into discs values (7, 3, 2, 2.59, 't');
insert into discs values (8, 3, 2, 2.59, 't');

create table customers (
    id      integer primary key,
    name    varchar(255),
    email   varchar(255)
);

insert into customers values (1, 'John', 'john@hotmail.com');
insert into customers values (2, 'Jane', 'jane@gmail.com');

create table rentals (
    id              integer primary key,
    customer_id     integer not null references customers(id),
    disc_id         integer not null references discs(id),
    date_rented     date,
    date_returned   date
);

insert into rentals values (1, 1, 7, '2013-10-01', '2013-10-03');
insert into rentals values (2, 2, 5, '2013-10-05', '2013-10-06');
insert into rentals values (3, 2, 2, '2013-11-02', null);
insert into rentals values (4, 2, 3, '2013-11-02', null);

create table ratings (
    customer_id integer not null references customers(id),
    movie_id    integer not null references movies(id),
    rating      integer,
  primary key (customer_id, movie_id)
);

insert into ratings values (1, 1, 1);
insert into ratings values (1, 2, 4);
insert into ratings values (1, 3, 5);
insert into ratings values (2, 1, 4);

我的逻辑是,我将获得将要插入或更新的评级 table 的新值,并使用这些值与租金 table 中的内容进行比较,看看是否客户已经租了那部电影,如果他们租了,那么他们可以输入评级。但我不能在这个大声笑中转移那个逻辑。除非有更简单的方法。

函数内的循环让事情变得有点复杂,让我们看看能不能去掉它。您的评分 table 参考了客户和电影,因此我们需要加入。

SELECT COUNT(*) INTO rented FROM rentals WHERE disc_id IN
  (SELECT id from discs INNER JOIN 
   rentals ON disc_id = discs.id where movie_id = new.movie_id)
  AND customer_id = new.customer_id 

对,这应该会使您的存储过程的逻辑更容易。我现在让你完成它,因为这毕竟是一个学习练习。

您需要这种连接,因为它比循环更有效、更简单。 ratings table 引用了 movie_id 但 rentals table 只有 disc_id 因此要了解用户是否租了特定电影,您需要加入它通过光盘 table.

您将需要更改 return 值。参考:http://www.postgresql.org/docs/9.2/static/plpgsql-trigger.html

Row-level triggers fired BEFORE can return null to signal the trigger manager to skip the rest of the operation for this row (i.e., subsequent triggers are not fired, and the INSERT/UPDATE/DELETE does not occur for this row). If a nonnull value is returned then the operation proceeds with that row value

另请注意,您没有在触发器函数内执行 INSERT。您只需 return 一个非空值即可让插入继续进行。

这是EXISTS()版本。 (顺便说一句:缺少电影的定义)

CREATE OR REPLACE FUNCTION rate_only_rented()
RETURNS TRIGGER AS $func$
BEGIN
IF ( NOT EXISTS (
        SELECT *
        FROM rentals r
        JOIN discs d ON r.disc_id = d.id
        WHERE d.movie_id = NEW.movie_id
        AND r.customer_id = NEW.customer_id
        ) )  THEN
        RAISE EXCEPTION 'you(%) have not rented this movie(%) before'
                             , NEW.customer_id             ,NEW.movie_id;
        RETURN NULL;
ELSE
        RETURN NEW;
END IF;
END;
$func$ language plpgsql;

触发器:

CREATE TRIGGER rate_only_rented
AFTER INSERT OR UPDATE
ON ratings
FOR EACH ROW
EXECUTE PROCEDURE  rate_only_rented()
        ;