【问题标题】:PLSQL Oracle Trigger that sums amount in rows by idPLSQL Oracle 触发器,按 id 对行中的金额求和
【发布时间】:2020-01-12 15:05:04
【问题描述】:

我有这样的表格:

运费表:

ProducernName| ... |Amount
A            |...  |1
B            |...  |2
A            |...  |5
C            |...  |3
C            |...  |5

还有另一个生产者表:

Name|...| Shipped Amount
A   |...|  NULL
B   |...|  NULL
C   |...|  NULL

我们的想法是创建一个触发器,将 Shippings 表中每个生产者的发货量相加,并在任何更改(更新、删除、插入)后在 Producers 表上更新它 如果触发器有效,Producers 表将如下所示:

Name|...| Shipped Amount
A   |...|  6
B   |...|  2
C   |...|  8

我尝试的是找到金额的总和并将其保存在我声明的变量中,但我不知道我的思维方向是否正确以及如何将总和插入到生产者表的正确行中..

CREATE TRIGGER SumShippedTrigger
AFTER INSERT OR DELETE OR UPDATE
OF amount
ON SHIPPINGS
FOR EACH ROW 
DECLARE
        producerShippedAmount NUMBER(10);
        producer VARCHAR2(10);
BEGIN
        SELECT SUM(amount) INTO producerShippedAmount 
        FROM Shippings
        WHERE producer= :NEW.producer;


    producer:= :NEW.procuder;
    END;

/

如果有人可以帮助我,我将不胜感激! :)

【问题讨论】:

  • 设计规则 #2749:不要存储汇总数据。你将在职业生涯的剩余时间里找出错误的原因。相反,在需要时汇总数据 - 至少暂时是正确的。

标签: sql oracle plsql database-trigger


【解决方案1】:

为什么要为此使用触发器?是不是数据太大了,简单的聚合不可行:

select producer, sum(amount)
from shippings
group by producer;

您可以轻松地为单个生产者查找此内容(使用 where),并使用索引使其更高效。

如果您确实使用触发器,我会说通常的方法是增量更改总和:

CREATE TRIGGER SumShippedTrigger
    AFTER INSERT OR DELETE OR UPDATE OF amount
    ON SHIPPINGS
    FOR EACH ROW 
BEGIN
    UPDATE producers
        SET amount = COALESCE(amount, 0) + COALESCE(:new.amount, 0) - COALESCE(:old.amount, 0)
        WHERE producer = COALESCE(:NEW.producer, :OLD.producer);
END;

虽然您的方法在逻辑上是正确的,但它所做的工作比必要的要多,并且可能会导致变异表触发器错误。

【讨论】:

  • 需要对@Gordon 代码稍作修改。触发器在 DELETE 上触发,然后 :new. 为空。所以 where 子句也需要合并它们。 " WHERE producer = coalesce(:NEW.producer;:Old.producer)
  • @Belayer 。 . .接得好。谢谢。
  • 非常感谢您的回复!但是我尝试了您的方法,但 Producers 表中的金额行没有任何变化(将所有值插入表后仍然为空)
  • @Ian 。 . .这似乎有些不对劲。如果名称匹配,则值至少应更改为0 而不是NULL。听起来触发器没有被调用。或者,匹配条件不会产生实际匹配。
  • @Ian:你提交更改了吗?
【解决方案2】:

为了下表:

运费表:

ProducernName| ... |Amount
A            |...  |1
B            |...  |2
A            |...  |5
C            |...  |3
C            |...  |5

另一个表是生产者:

Name|...| Amount
A   |...|  NULL
B   |...|  NULL
C   |...|  NULL

您可以像这样在producers 表上声明触发器:

create or replace trigger trg_producers 
BEFORE INSERT OR DELETE OR UPDATE
    ON producers
    FOR EACH ROW 
declare 
pragma autonomous_transaction;
producerShippedAmount number(10):=0;
begin

SELECT SUM(amount) INTO producerShippedAmount 
        FROM Shippings
        WHERE ProducernName = :NEW.Name;

:new.amount := producerShippedAmount;


end trg_producers;

有必要提一下,pragma automatic_transaction必须声明

【讨论】:

  • 触发器在我们对其拥有的表执行 DML 时触发。此触发器仅在 PRODUCERS 记录更改时更新 PRODUCERS.AMOUNT。这显然不是 OP 的意图:他们想要更新 PRODUCERS 表当更新 SHIPPERS 表时
  • 好的,这种方式是正确的,为了改变PRODUCERS表和改变SHIPPERS表一样,所以哪个适合你取决于你的业务
  • 即使不使用触发器也可以达到目标。我建议您可以在服务器端处理它,而不是使用触发器。使用触发器不是合适的解决方案
  • 当你说“服务器端”是什么意思?数据库服务器可以被认为是“服务器端”,那么你建议什么?