对于学校项目,我们被迫拥有冗余信息并使用触发器进行更新。我们有一个名为'recipe_ratings'的表,其中包含'rating'(数字0-100)。在我们的“食谱”表中,我们有一个名为“评级”的冗余行,其中包含该特定食谱的平均评级。
我们试图像这样创建一个Oracle触发器:
CREATE OR REPLACE TRIGGER trigger_rating
AFTER UPDATE
ON recipe_ratings
FOR EACH ROW
DECLARE
average_rating NUMBER;
BEGIN
SELECT ROUND(AVG(rating))
INTO average_rating
FROM recipe_ratings
WHERE rid = :new.rid;
UPDATE recipe SET rating = average_rating
WHERE rid = :new.rid
END;
但是这给了我们:ORA-04091:表名是变异的,触发器/函数可能看不到它。我们正在尝试“自动交易”,但感觉我们正在偏离触发器。
我们如何使这个触发器有效?
答案 0 :(得分:4)
我希望教授不要引导你走自然交易的道路,除了使用无效的数据模型之外,这将是对自治交易的可怕滥用。
在现实世界中,为了使这种事情发挥作用,你需要
RECIPE_RATINGS
表显然,这种事情很快变得非常繁琐,这就是存储冗余数据的问题。
如果您只需要处理插入,并且可以使用INSERT ... VALUES保证所有插入都是单行插入,则可以查询查询中的RECIPE_RATINGS
表。这在现实世界中不起作用,但在教室中可能就足够了。
如果您不介意每次更新RECIPE_RATINGS
中的单行时重新计算每个配方的平均评级 - 这在实践中会是灾难性的,但可能会有效在一个足够小的数据集上 - 你可以有一个after语句触发器,它在RECIPE
表的每一行上进行相关更新。
答案 1 :(得分:2)
您的数据模型有多灵活?
您可以存储所有评级的总和加上评级的数量,而不是将平均评级存储在食谱上。
对评级的插入触发器将使用值或新行更新父级食谱行,以将评级添加到总计,将1添加到评级的数量/计数。
更新触发器会将:NEW和:OLD值之间的差异添加到总数中(而不是更新计数)。
两个触发器都不必查询评级表上的其他行,以防止变异表错误,并且在具有多个并发用户的环境中使用它会更安全。
查询(或视图或派生列)只需将总数除以计数即可确定平均值。
答案 2 :(得分:1)
This article提供了一种避免这些错误的方法。
另一个想法 - 一个'正常'的触发器,比FOR FOR EACH ROW触发器更适合这里吗?如果在一个语句中对同一个配方有多个recipe_rating更新,则计算多次平均值(否则会突变警告)。