Oracle使用触发器计算平均值

时间:2011-05-03 22:41:59

标签: oracle triggers ora-04091

对于学校项目,我们被迫拥有冗余信息并使用触发器进行更新。我们有一个名为'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:表名是变异的,触发器/函数可能看不到它。我们正在尝试“自动交易”,但感觉我们正在偏离触发器。

我们如何使这个触发器有效?

3 个答案:

答案 0 :(得分:4)

我希望教授不要引导你走自然交易的道路,除了使用无效的数据模型之外,这将是对自治交易的可怕滥用。

在现实世界中,为了使这种事情发挥作用,你需要

  • 包含RID值集合的包
  • 初始化此集合的before语句触发器
  • 将:new.rid值插入集合
  • 的行级触发器
  • 一个after语句触发器,用于读取集合并在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更新,则计算多次平均值(否则会突变警告)。