【问题标题】:After insert update trigger is doubling the values插入更新触发器后,值加倍
【发布时间】:2020-12-12 19:31:40
【问题描述】:

我正在尝试做一个触发器,在插入另一个字段时更新一个字段。表格如下图所示。

These are my tables

所以,每当在“transacoes”表中插入交易(transacoes 表示交易)时,我都必须更新 contas 表中的“saldo”(葡萄牙语中的余额)。如果类别(表示类别)表中的“tipo_de_movimento”(表示交易类型)等于“收入”(realizada 表示已完成),我应该将该值加到“saldo”字段中,如果“tipo_de_movimento”='Despesa'(表示费用),我应该将该值减去“saldo”字段。

所以,我创建了这个触发器

ON [dbo].[TRANSACOES]

AFTER INSERT, UPDATE
AS

begin

update CONTAS set saldo = saldo + i.VALOR
from inserted i, TRANSACOES t, contas c, CATEGORIAS cat
where i.IDTRANSACAO= t.IDTRANSACAO 
and i.ESTADO = 'Realizada' 
and i.IDCONTA = c.IDCONTA
and i.IDCATEGORIA = cat.IDCATEGORIA
and  cat.TIPO_DE_MOVIMENTO ='Receita'

update CONTAS set saldo = saldo - i.VALOR
from inserted i, TRANSACOES t, contas c, CATEGORIAS cat
where i.IDTRANSACAO= t.IDTRANSACAO 
and i.ESTADO = 'Realizada' 
and i.IDCONTA = c.IDCONTA
and i.IDCATEGORIA = cat.IDCATEGORIA
and  cat.TIPO_DE_MOVIMENTO ='Despesa'

end

但我遇到的问题是,每当我在 transacoes 中插入一个值时,我的 saldo 字段是它应该是的两倍。

所以,如果我插入这个:


INSERT INTO TRANSACOES VALUES(4,8,2,1000,'20201210','Teste Right','Transfer','Recibo','Realizada')

添加到saldo字段的值应该是1000,但是它添加了2000,所以它添加了两次值(?)

那么,谁能告诉我这里发生了什么?

我有点不知道为什么会发生这种情况,我是触发器的新手,所以如果我没有提供足够的细节,我很抱歉。

非常感谢!

【问题讨论】:

  • Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(25 多年前),不鼓励使用它
  • 是否有可能在插入事务行之后,作为流程的一部分,您正在更新同一行上的某些内容?
  • 与其为此编写触发器,不如考虑在视图中放置逻辑来计算余额。如果必要,您还可以将其设为索引视图,以便 SQL Server 自动重新计算和存储余额。手动维护此余额信息只会增加余额与原始数据不一致的可能性(正如您所发现的那样)

标签: sql-server triggers


【解决方案1】:

通常,更新每行只设置一个/单个值(即使有多个值匹配该行)。 TRANSACOES 触发器中的更新不可能每行多次更新 CONTAS。很可能在 TRANSACOES 上存在第二个触发器,并且该第二个触发器再次触发并更新 CONTAS,或者 CONTAS 上的触发器再次添加该值。

“更新每行只设置一个/单个值”这一事实将在触发器中导致另一个问题:
如果您尝试为同一个 IDCONTA 插入两个事务,则只有两个 VALOR 之一将添加到 SALDO(== 两个匹配 CONTA.IDCONTA 的值,但更新只能为每行设置 一个 值)

insert TRANSACOES (IDCONTA, VALOR)
values(123, 500), (123, 1000) 
--then the update in the trigger (as it is written) 
--will either update CONTA.IDCONTA[123] with +500 or +1000, but not with +1500.

另一个问题是 TRANSACOES 中现有行的更新:如果现有的 'Realizada' & 'Receita' 事务从 VALOR = 140 --> VALOR = 150 更新,则相应的 CONTA 应增加 (150-140 =+10),但触发器的更新将 CONTA 设置为 +150 (=inserted.VALOR)。

在创建 FOR INSERT 的触发器中,UPDATE --> 如果插入或更新了任何行,则插入的“表”将具有值。删除的“表”对于插入将始终为空。如果您想要一个用于插入和更新 DML 操作的更新语句,则应将插入的语句与已删除的语句保持连接。
触发器中更新语句的一种形式可能是:

...........trigger AFTER INSERT, UPDATE

--if inserted is "empty", no rows were inserted or updated..just do nothing, return
if not exists(select * from inserted)
begin
    return
end

update c
set saldo = saldo + isnull(aggr.contaVALOR, 0) --better safe (where filters out null)
from CONTAS as c
JOIN
(
    select 
        i.IDCONTA, 
        sum
        (
            /*if inserted/transaction is 'Realizada'&'Receita' add i.VALOR, in any other case NULL::ISNULL(null, 0) = 0*/
            isnull(case when i.ESTADO = 'Realizada' and icat.TIPO_DE_MOVIMENTO = 'Receita' then i.VALOR end, 0)
            -  /*<--subtract*/
            /*if deleted/transaction was 'Realizada'&'Receita' subtract d.VALOR, in any other case NULL::ISNULL(null, 0) = 0*/
            isnull(case when d.ESTADO = 'Realizada' and dcat.TIPO_DE_MOVIMENTO = 'Receita' then d.VALOR end, 0)
        ) as contaVALOR
    from inserted as i
        join CATEGORIAS as icat on i.IDCATEGORIA = icat.IDCATEGORIA
    left join 
    (
        deleted as d 
        join CATEGORIAS as dcat on d.IDCATEGORIA = dcat.IDCATEGORIA
    ) on i.IDTRANSACAO = d.IDTRANSACAO
    where 'Realizada' in (i.ESTADO, d.ESTADO) --status of transaction is or changed from/to Realizada
      and 'Receita' in (icat.TIPO_DE_MOVIMENTO, dcat.TIPO_DE_MOVIMENTO) --category of transaction is or changed from/to 'Receita'
    group by i.IDCONTA
) as aggr on c.IDCONTA = aggr.IDCONTA
where aggr.contaVALOR <> 0;

即使上述方法适用于新事务或更新/更改现有事务(包括状态和类别)[注意:代码根本没有执行,没有经过任何测试],保持平衡的方法有一个主要警告带触发器:如果现有的 CATEGORIA 从 'Receita' 更改为 'xyz' 会发生什么? CATEGORIAS 表得到更新,但这不会触发任何 TRANSACOES 触发器,并且所有计算出的 SALDO 将立即失效。

正如问题中所评论的,保持与触发器的实时平衡很棘手,并且很快就会变得混乱。此外,平衡逻辑分布在多个地方(例如,您需要两个触发器来保持某种准确的萨尔多,两个地方关注萨尔多)。

视图会将所有逻辑整合到一个位置/语句中,并且在需要更改此逻辑时提供更好的控制。

【讨论】:

    猜你喜欢
    • 2021-03-08
    • 1970-01-01
    • 2016-10-13
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多