2

对于学校项目,我们被迫拥有冗余信息并使用触发器对其进行更新。我们有一个名为“recipe_ratings”的表,其中包含一个“评级”(数字 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:表名正在变化,触发器/函数可能看不到它。我们正在试验“自主交易”,但感觉就像我们正在远离我们的触发器。

我们怎样才能使这个触发器起作用?

4

3 回答 3

4

我希望教授不会带您走上使用自主事务的道路,这除了使用无效的数据模型外,还会严重滥用自主事务。

在现实世界中,为了让这种事情起作用,你需要

  • 具有 RID 值集合的包
  • 初始化此集合的 before 语句触发器
  • 将 :new.rid 值插入集合的行级触发器
  • 一个 after 语句触发器,它读取集合并在RECIPE_RATINGS表上发布更新

显然,这类事情很快就会变得相当麻烦,这就是为什么存储冗余数据如此成问题的原因。

如果您只需要处理插入,并且可以使用 INSERT ... VALUES 保证所有插入都是单行插入,那么您可以在查询RECIPE_RATINGS中查询表。这在现实世界中行不通,但在课堂上可能就足够了。

如果您不介意每次更新单行时重新计算每个RECIPE_RATINGS配方的平均评分——这在实践中将是灾难性的,但可能适用于足够小的数据集——你可以有一个 after 语句触发器对RECIPE表的每一行进行相关更新。

于 2011-05-03T22:55:47.667 回答
2

您的数据模型有多灵活?

您可以存储所有评分的总和加上评分的数量,而不是存储食谱上的平均评分。

评级上的插入触发器将采用值或新行来更新父配方行以将评级添加到总数中并将 1 添加到评级的数量/计数中。

更新触发器会将 :NEW 和 :OLD 值之间的差异添加到总数中(而不是更新计数)。

两个触发器都不必查询评级表上的其他行,以防止变异表错误,并使其在具有多个并发用户的环境中使用更加安全。

查询(或视图或派生列)将简单地通过将总数除以计数来确定平均值。

于 2011-05-04T00:25:13.493 回答
1

本文提供了一种避免这些错误的方法。

另一个想法——“正常”触发器会比 FOR EACH ROW 触发器更适合这里吗?如果在一个语句中对同一配方有多个 recipe_rating 更新,则您将多次计算平均值(因此会出现突变警告)。

于 2011-05-03T22:51:49.023 回答