【发布时间】:2022-12-11 12:48:59
【问题描述】:
我正在尝试在 Postgresql 中正确实现触发器功能。数据库由三个表组成,其中两个由第三个连接,第三个是中介:
表 MUSICIAN(PK = id_musician)
| id_musician | name | birth | death | gender | nationality | num_awards |
|---|---|---|---|---|---|---|
| 1 | John Lennon | 1940-10-09 | 1980-12-08 | M | British | 0 |
| 2 | Paul McCartney | 1942-06-18 | NULL | M | British | 0 |
| 3 | Joep Beving | 1976-01-09 | NULL | M | Dutch | 0 |
| 4 | Amy Winehouse | 1983-09-14 | 2011-07-23 | F | British | 0 |
| 5 | Wim Mertens | 1953-05-14 | NULL | M | Belgian | 0 |
作曲家 (PK = FK = id_musician, id_song)
| id_musician | id_song |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 4 | 4 |
| 5 | 5 |
表歌(PK = id_song;FK = id_album)
| id_song | title | duration | id_album | awards |
|---|---|---|---|---|
| 1 | A Hard Day's Night | 00:02:34 | 1 | 1 |
| 2 | Henosis | 00:06:44 | 2 | 1 |
| 3 | Rehab | 00:03:34 | 3 | 6 |
| 4 | Back To Black | 00:04:01 | 3 | 2 |
| 5 | Close Cover | 00:03:31 | 4 | 0 |
在表SONG上实现触发函数,计算表MUSICIAN中的列num_awards,预期结果如下:
| id_musician | name | birth | death | gender | nationality | num_awards |
|---|---|---|---|---|---|---|
| 1 | John Lennon | 1940-10-09 | 1980-12-08 | M | British | 1 |
| 2 | Paul McCartney | 1942-06-18 | NULL | M | British | 1 |
| 3 | Joep Beving | 1976-01-09 | NULL | M | Dutch | 1 |
| 4 | Amy Winehouse | 1983-09-14 | 2011-07-23 | F | British | 8 |
| 5 | Wim Mertens | 1953-05-14 | NULL | M | Belgian | 0 |
如您所见,列 num_awards 汇总了表 SONG 中每个作曲家的所有歌曲的奖项。为此,我编写了以下触发函数:
CREATE OR REPLACE FUNCTION update_num_awards()
RETURNS trigger AS $$
BEGIN
CREATE TABLE IF NOT EXISTS bakcomp AS TABLE COMPOSER;
CREATE TABLE IF NOT EXISTS baksong AS TABLE SONG;
IF (TG_OP = 'INSERT') THEN
UPDATE MUSICIAN
SET num_awards = num_awards + NEW.awards
WHERE id_musician IN (SELECT c.id_musician
FROM COMPOSER AS c JOIN SONG
ON(c.id_song = NEW.id_song));
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE MUSICIAN
SET num_awards = num_awards + NEW.awards
WHERE id_musician IN (SELECT c.id_musician
FROM COMPOSER AS c JOIN SONG
ON(c.id_song = NEW.id_song));
UPDATE MUSICIAN
SET num_awards = num_awards - OLD.awards
WHERE id_musician IN (SELECT c.id_musician
FROM bakcomp AS c JOIN baksong
ON(c.id_song = OLD.id_song));
ELSIF (TG_OP = 'DELETE') THEN
UPDATE MUSICIAN
SET num_awards = num_awards - OLD.awards
WHERE id_musician IN (SELECT c.id_musician
FROM bakcomp AS c JOIN baksong
ON(c.id_song = OLD.id_song));
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE CONSTRAINT TRIGGER trigger_update_num_awards AFTER INSERT OR DELETE OR UPDATE ON SONG
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW EXECUTE PROCEDURE update_num_awards();
该函数在表 SONG 中插入、删除或更新后触发。插入时,编码似乎非常简单,但是当涉及到删除时,事情开始变得困难——我编码的子查询不起作用,因为行可能已经从 COMPOSER 和 SONG 表中消失了。我试过使用临时表,但它们似乎不起作用——不知何故,它们在删除操作开始前就消失了。因此,我唯一的解决方案是创建两个永久辅助表 bakcomp 和 baksong,以便在每次删除操作之前制作 COMPOSER 和 SONG 的副本。
我的问题是,在这种情况下如何执行临时表?就我个人而言,尽管我的一段代码可以正确地满足我的需求,但我还是想改进它或使其更简单和优雅。
任何帮助将不胜感激。
问候
【问题讨论】:
标签: sql postgresql