更新特定列后的PostgreSQL触发器

sam*_*mba 1 sql postgresql triggers database-design

我正在探索触发器,并且希望创建一个在game_saved列上的Update事件后触发的触发器。正如我在PostgreSQL文档中所读的那样,可以为列创建触发器。该列包含boolean值,因此用户可以将游戏添加到他的收藏中或将其删除。因此,我希望触发函数能够game_saved为特定用户计算列中设置为TRUE的游戏数量。然后total_game_count在一个game_collection表中更新。

game_collection

id - BIGSERIAL primary key
user_id - INTEGER REFERENCES users(id)
total_game_count - INTEGER
Run Code Online (Sandbox Code Playgroud)

game_info

id - BIGSERIAL primary key
user_id - INTEGER REFERENCES users(id)
game_id - INTEGER REFERENCES games(id)
review - TEXT
game_saved - BOOLEAN
Run Code Online (Sandbox Code Playgroud)

这是我的触发器(它不起作用,我想找出原因):

CREATE OR REPLACE FUNCTION total_games() 
RETURNS TRIGGER AS $$ 
BEGIN 
UPDATE game_collection 
SET total_game_count = (SELECT COUNT(CASE WHEN game_saved THEN 1 END)
                        FROM game_info WHERE game_collection.user_id = game_info.user_id)
WHERE user_id = NEW.user_id; 
RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER tr_total_games 
AFTER UPDATE OF game_saved FOR EACH ROW 
EXECUTE PROCEDURE total_games();
Run Code Online (Sandbox Code Playgroud)

如果我将AFTER UPDATE OF game_saved(列)更改为AFTER UPDATE ON game_info(表),则触发器可以正常工作。因此,创建专门用于列更新的触发器存在一些问题。

在列更新上触发触发器是一个好主意,还是我应该在这里寻找另一种方法?

Erw*_*ter 6

语法为:

CREATE TRIGGER tr_total_games 
AFTER UPDATE OF game_saved ON game_info
FOR EACH ROW 
EXECUTE PROCEDURE total_games();
Run Code Online (Sandbox Code Playgroud)

(如手册中所述。)

但是整个方法是可疑的。在并发写入负载下,通过触发器使聚合保持最新状态容易出错。

没有并发写入负载,有更简单的解决方案:只需从当前总数中加/减1 ...

可靠的解决方案是VIEWgame_collection.total_game_count完全删除该列,也可能删除整个表game_collection,这似乎没有其他用途。

CREATE VIEW v_game_collection AS
SELECT user_id, count(*) AS total_game_count
FROM   game_info
WHERE  game_saved
GROUP  BY user_id;
Run Code Online (Sandbox Code Playgroud)

这将返回在game_info其中至少有1行的所有用户game_saved IS TRUE(并忽略所有其他用户)。

对于非常大的表,您可能需要MATERIALIZED VIEW或相关的解决方案来提高读取性能。

  • 谢谢你的精彩回答。它帮助我理解了更好的方法,了解了视图并将它们整合到我的项目中。 (3认同)