【问题标题】:Update rows in a summary table with min and max values from a related table using a trigger使用触发器使用相关表中的最小值和最大值更新汇总表中的行
【发布时间】:2020-06-15 23:51:24
【问题描述】:

我有两张桌子,OrderSlot。每个插槽都有时间,但为简单起见,我们假设它们是整数。像这样的:

Order              Slot
-----              ----
order_id           slot_id
earliest           order_id
latest             time_from
                   time_to

我正在努力保持earliestlatest 更新,以便earliest = MIN(slot.time_from)latest = MAX(time_to)time_from < time_to 总是如此,但这在这里不重要。

为此,我在 Slot 上创建了 UPDATE 和 INSERT 触发器。触发器:

CREATE OR REPLACE FUNCTION keep_orders_updated() RETURNS trigger AS $trigger$
BEGIN

  UPDATE "order"
  SET earliest=d.earliest_min, latest=d.latest_max
  FROM (SELECT MIN(time_from) as earliest_min, MAX(time_to) AS latest_max FROM "slot" WHERE order_id = NEW.order_id) AS d
  WHERE order_id=NEW.order_id;

  RETURN NULL;
END;
$trigger$ LANGUAGE plpgsql;

CREATE TRIGGER keep_orders_updated_trigger AFTER INSERT OR UPDATE ON "slot" FOR EACH ROW EXECUTE PROCEDURE keep_orders_updated();

它在大多数情况下都会产生正确的结果,除了以下我正在尝试修复的情况。我们从这些数据开始:

Order:
  order_id |   earliest    | latest     
 ----------+------------------------
     1     | 10            | 20


Slots:

  slot_id  | order_id | time_from   | latest         
 ----------+------------------------+------------------------
     1     | 1        | 10          | 12
     1     | 1        | 18          | 20

并且在两个并发事务中(在我的情况下,它们使用来自 Web 服务器中连接池的不同连接运行,基本上服务于大约同时到达的两个请求):

tx1: UPDATE "slot" SET time_from=15 AND time_to=17;
tx2: UPDATE "slot" SET time_from=19 AND time_to=25;

我得到正确更新的插槽(预期),并且错误地更新了顺序。仅最新更新:

Order:
  order_id |   earliest    | latest     
 ----------+------------------------
     1     | 10            | 25

同样,如果我将 time_from 设置为小于 10 的值,它会更新。

我想我明白为什么会发生这种情况。相关行上的每个槽更新块,允许更新同时运行。触发器只看到其事务更新的数据,而不是其他事务更新的数据。因此,它会计算 tx1 的 MIN(10, 15) 和 tx2 的 MIN(10, 19),并按相关顺序将最早设置为 10。

我的理解正确吗?如何使这样的配置正常工作?

【问题讨论】:

  • 我想我通过在按订单进行更新之前选择所有用于更新的插槽来修复它 (PERFORM slot_id FROM slot WHERE order_id=NEW.order_id FOR UPDATE)。在此之前,我尝试使用插槽的 order_id 使用咨询锁,但没有奏效。

标签: sql postgresql date concurrency database-trigger


【解决方案1】:

与其存储这些派生信息,不如直接创建一个视图?

create view v_orders as
select order_id, min(time_from) earlierst, max(time_to) latest
from slots
group by order_id

使用此技术,您无需担心实际维护数据。该视图为您的数据提供始终最新的视角。

【讨论】:

  • 我使用的是一个有点傻的 ORM (sqlboiler),它不支持视图(至少不容易),并且应用程序的很大一部分围绕订单和那些日期。我还将最早和最新的触发器用于不同的触发器(我需要知道它们何时发生变化 - 最初写道这是可行的,但我实际上不确定如何去做)。
  • @VladV 。 . . ORM 怎么可能不支持视图?您可以在数据库中定义视图,ORM 应该只看到一个只读表。
  • 我知道这很奇怪。它基于 DB 模式生成代码,完全忽略视图。甚至还有a $100 bounty 某人to implement that。当然,我可以手动使用它们。
  • 忽略 ORM,有没有办法检测 earliest 何时在此设置中更改?我目前为此使用订单更新触发器。
猜你喜欢
  • 1970-01-01
  • 2011-04-03
  • 2012-02-07
  • 2017-10-13
  • 2018-03-05
  • 1970-01-01
  • 2016-10-14
  • 2021-09-17
  • 1970-01-01
相关资源
最近更新 更多