Oracle 使用触发器计算平均值

发布于 2024-11-05 08:28:56 字数 594 浏览 0 评论 0原文

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

我们怎样才能让这个触发器发挥作用呢?

For a school project we are forced to have redundant information and update it by using triggers. We have a table called 'recipe_ratings' that contains a 'rating' (numbers 0-100). In our 'recipes' table we have a redundant row called 'rating' that contains the average rating for that specific recipe.

We tried to create an Oracle trigger like this:

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;

But this gives us: ORA-04091: table name is mutating, trigger/function may not see it. We are experimenting with 'autonomous transaction' but it feels like we're drifting away from our trigger.

How can we make this trigger work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青衫负雪 2024-11-12 08:28:56

我希望教授不会引导您走上使用自治事务的道路,这除了使用无效的数据模型之外,这将是对自治事务的可怕滥用。

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

  • 一个包含 RID 值集合的包
  • 一个用于初始化该集合的 before 语句触发器一个
  • 用于将 :new.rid 值插入集合中的行级触发器
  • 一个 after 语句触发器读取集合并在 RECIPE_RATINGS 表上发出更新

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

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

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

I hope the professor is not leading you down the path of using autonomous transactions which would be a hideous misuse of autonomous transactions in addition to using an invalid data model.

In the real world, in order for this sort of thing to work, you would need

  • A package with a collection of RID values
  • A before statement trigger that initializes this collection
  • A row-level trigger that inserts the :new.rid values into the collection
  • An after statement trigger that reads through the collection and issues the updates on the RECIPE_RATINGS table

Obviously, that sort of thing gets quite cumbersome quite quickly which is why storing redundant data is so problematic.

If you only had to handle inserts and you could guarantee that all inserts would be single-row inserts using the INSERT ... VALUES, you could query the RECIPE_RATINGS table in your query. That doesn't work in the real world, but it may suffice in a classroom.

If you don't mind re-computing the average rating for every recipe every time a single row in RECIPE_RATINGS is updated-- something that would be catastrophic in practice but may work on a sufficiently small data set-- you could have an after statement trigger that does a correlated update on every row of the RECIPE table.

此生挚爱伱 2024-11-12 08:28:56

您的数据模型有多灵活?

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

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

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

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

查询(或视图或派生列)只需将总数除以计数即可确定平均值。

How flexible is your data model ?

Rather than the storing the average rating on the recipe, can you store the total of all the rating plus the number of ratings.

An insert trigger on ratings would take the values or the new row to update the parent recipe row to add the rating to the total and 1 to the number/count of ratings.

An update trigger would add the difference between the :NEW and :OLD values to the total (and not update the count).

Neither trigger has to query other rows on the ratings table preventing the mutating table error, and making it much safer to use in an environment with multiple concurrent users.

The query (or a view or a derived column) would determine the average simply by dividing the total by the count.

轻拂→两袖风尘 2024-11-12 08:28:56

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

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

This article gives one means of avoiding these errors.

Another thought - would a 'normal' trigger, rathen than a FOR EACH ROW trigger be more suitable here? If there are multiple recipe_rating updates for the same recipe in one statement you're calculating the average multiple times otherwise (hence the mutation warning).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文