触发和功能问题
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()
;
我目前正在经历尝试学习函数和触发器的成长过程中的痛苦。我正在尝试做一本正在阅读的书中的问题,但我不明白如何做某些部分。
使用这个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()
;