更新所有行时出错

Vin*_*oni 3 trigger sql-server-2005 sql-server

当我更新单行时,它工作正常。但是当我更新所有行时:

 UPDATE cad_bilhetes
 SET ligacao_acobrar = 'False'
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

消息 512,级别 16,状态 1,过程 TG_CAD_BILHETES_UPDATE,第 34 行子查询返回了 1 个以上的值。当子查询跟随 =、!=、<、<=、>、>= 或当子查询用作表达式时,这是不允许的。该语句已终止。

在 TG_CAD_BILHETES_UPDATE 的第 34 行中有:

DECLARE @VALOR_LIGACOES_DDI_ATUAL DECIMAL(7,2)
Run Code Online (Sandbox Code Playgroud)

触发器的开始:

ALTER TRIGGER [dbo].[TG_CAD_BILHETES_UPDATE]
ON [dbo].[CAD_BILHETES]
INSTEAD OF UPDATE
AS
BEGIN
DECLARE @ID INT
DECLARE @VALOR_BILHETE_NOVO DECIMAL(7,2)
DECLARE @VALOR_BILHETE_ATUAL DECIMAL(7,2)
DECLARE @ENCONTROU INT
DECLARE @ID_CONTRATACAO INT
DECLARE @TIPO VARCHAR(2)
DECLARE @TIPO_APARELHO VARCHAR(1)
DECLARE @ID_COMPETENCIA INT
DECLARE @ID_CONTRATACAO_ATUAL INT
DECLARE @ID_CONTRATACAO_NOVO INT
DECLARE @VALOR_LIGACOES_DDD_FIXO_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_DDD_MOVEL_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_LOCAL_MOVEL_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_LOCAL_FIXO_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_DDI_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_VOIP_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_TOM_REMOTO_NOVO DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_DDD_FIXO_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_DDD_MOVEL_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_LOCAL_MOVEL_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_LOCAL_FIXO_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_DDI_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_VOIP_ATUAL DECIMAL(7,2)
DECLARE @VALOR_LIGACOES_TOM_REMOTO_ATUAL DECIMAL(7,2)
DECLARE @VALOR_OS_NOVO DECIMAL(7,2)
DECLARE @FOI_CANCELADO_NOVO BIT

SET @ENCONTROU = 0
SET @ID = (SELECT ID FROM INSERTED)
SET @ID_COMPETENCIA = (SELECT ID_COMPETENCIA FROM INSERTED)
SET @VALOR_LIGACOES_DDD_FIXO_NOVO = 0
SET @VALOR_LIGACOES_DDD_MOVEL_NOVO = 0
SET @VALOR_LIGACOES_LOCAL_MOVEL_NOVO = 0
SET @VALOR_LIGACOES_LOCAL_FIXO_NOVO = 0
SET @VALOR_LIGACOES_DDI_NOVO = 0
SET @VALOR_LIGACOES_VOIP_NOVO = 0
SET @VALOR_LIGACOES_TOM_REMOTO_NOVO = 0
SET @VALOR_LIGACOES_DDD_FIXO_ATUAL = 0
SET @VALOR_LIGACOES_DDD_MOVEL_ATUAL = 0
SET @VALOR_LIGACOES_LOCAL_MOVEL_ATUAL = 0
SET @VALOR_LIGACOES_LOCAL_FIXO_ATUAL = 0
SET @VALOR_LIGACOES_DDI_ATUAL = 0
SET @VALOR_LIGACOES_VOIP_ATUAL = 0
SET @VALOR_LIGACOES_TOM_REMOTO_ATUAL = 0

SELECT TOP (1)
       @TIPO = SUBSTRING(B.TIPO, 2, 2), @TIPO_APARELHO = SUBSTRING(B.TIPO, 4, 1),
       @VALOR_BILHETE_ATUAL = B.VALOR,
       @ID_CONTRATACAO_ATUAL = C.ID
FROM CAD_BILHETES AS B
JOIN CAD_CONTRATACAO AS C
ON B.ID_PRODUTO = C.ID_PRODUTO AND (B.DATA_HORA >= C.DATA_INICIO AND (C.DATA_FIM IS NULL OR B.DATA_HORA <= C.DATA_FIM))  
WHERE B.ID = @ID and 
      C.FOI_CANCELADA= 'FALSE'

IF @ID_CONTRATACAO_ATUAL IS NOT NULL
BEGIN
    IF(@TIPO = 'IV')
        BEGIN
            SET @VALOR_LIGACOES_VOIP_ATUAL = @VALOR_BILHETE_ATUAL
        END
    ELSE
    IF(@TIPO = 'IT')
        BEGIN
            SET @VALOR_LIGACOES_TOM_REMOTO_ATUAL = @VALOR_BILHETE_ATUAL
        END
    ELSE
    IF(@TIPO = 'EL')
        BEGIN
            IF(@TIPO_APARELHO = 'F')
            BEGIN
                SET @VALOR_LIGACOES_LOCAL_FIXO_ATUAL = @VALOR_BILHETE_ATUAL
            END
            ELSE
            IF(@TIPO_APARELHO = 'M')
            BEGIN
                SET @VALOR_LIGACOES_LOCAL_MOVEL_ATUAL = @VALOR_BILHETE_ATUAL
            END
        END
    ELSE
    IF(@TIPO = 'EN')
        BEGIN
            IF(@TIPO_APARELHO = 'F')
            BEGIN
                SET @VALOR_LIGACOES_DDD_FIXO_ATUAL = @VALOR_BILHETE_ATUAL
            END
            ELSE
            IF(@TIPO_APARELHO = 'M')
            BEGIN
                SET @VALOR_LIGACOES_DDD_MOVEL_ATUAL = @VALOR_BILHETE_ATUAL
            END
        END
    ELSE
    IF(@TIPO = 'EI')
        BEGIN
            SET @VALOR_LIGACOES_DDI_ATUAL = @VALOR_BILHETE_ATUAL
        END
END

SELECT @TIPO = SUBSTRING(I.TIPO, 2, 2), @TIPO_APARELHO = SUBSTRING(I.TIPO, 4, 1),
       @VALOR_BILHETE_NOVO = I.VALOR,
       @ID_CONTRATACAO_NOVO = C.ID,
       @FOI_CANCELADO_NOVO = I.FOI_CANCELADO,   
       @ID_COMPETENCIA = I.ID_COMPETENCIA   
FROM INSERTED AS I
JOIN CAD_CONTRATACAO AS C 
ON I.ID_PRODUTO = C.ID_PRODUTO AND (I.DATA_HORA >= C.DATA_INICIO AND (I.DATA_HORA <= C.DATA_FIM  OR C.DATA_FIM IS NULL)) 
WHERE C.FOI_CANCELADA= 'FALSE'

IF @ID_CONTRATACAO_NOVO IS NOT NULL
BEGIN
    IF(@TIPO = 'IV')
        BEGIN
            SET @VALOR_LIGACOES_VOIP_NOVO = @VALOR_BILHETE_NOVO
        END
    ELSE
    IF(@TIPO = 'IT')
        BEGIN
            SET @VALOR_LIGACOES_TOM_REMOTO_NOVO = @VALOR_BILHETE_NOVO       
        END
    ELSE
    IF(@TIPO = 'EL')
        BEGIN
            IF(@TIPO_APARELHO = 'F')
            BEGIN
                SET @VALOR_LIGACOES_LOCAL_FIXO_NOVO = @VALOR_BILHETE_NOVO
            END
            ELSE
            IF(@TIPO_APARELHO = 'M')
            BEGIN
                SET @VALOR_LIGACOES_LOCAL_MOVEL_NOVO = @VALOR_BILHETE_NOVO  
            END
        END
    ELSE
    IF(@TIPO = 'EN')
        BEGIN
            IF(@TIPO_APARELHO = 'F')
            BEGIN
                SET @VALOR_LIGACOES_DDD_FIXO_NOVO = @VALOR_BILHETE_NOVO
            END
            ELSE
            IF(@TIPO_APARELHO = 'M')
            BEGIN
                SET @VALOR_LIGACOES_DDD_MOVEL_NOVO = @VALOR_BILHETE_NOVO
            END
        END
    ELSE
    IF(@TIPO = 'EI')
        BEGIN
            SET @VALOR_LIGACOES_DDI_NOVO = @VALOR_BILHETE_NOVO
        END

SELECT @ENCONTROU = 1
FROM   CAD_CUSTO_PRODUTO
WHERE ID_CONTRATACAO = @ID_CONTRATACAO_NOVO AND
ID_COMPETENCIA=@ID_COMPETENCIA

IF(@ENCONTROU = 0)
    BEGIN
        INSERT INTO CAD_CUSTO_PRODUTO
        VALUES (@ID_CONTRATACAO_NOVO, @ID_COMPETENCIA, NULL, 0, 0, 0, @VALOR_LIGACOES_DDD_FIXO_NOVO, 
                @VALOR_LIGACOES_DDD_MOVEL_NOVO, @VALOR_LIGACOES_LOCAL_MOVEL_NOVO, @VALOR_LIGACOES_LOCAL_FIXO_NOVO, 
                @VALOR_LIGACOES_DDI_NOVO, @VALOR_LIGACOES_VOIP_NOVO, @VALOR_LIGACOES_TOM_REMOTO_NOVO)
    END
ELSE
BEGIN
    IF (@FOI_CANCELADO_NOVO = 1)
    BEGIN
        UPDATE CAD_CUSTO_PRODUTO
        SET VALOR_LIGACOES_DDD_FIXO = VALOR_LIGACOES_DDD_FIXO - @VALOR_LIGACOES_DDD_FIXO_ATUAL,
            VALOR_LIGACOES_DDD_MOVEL = VALOR_LIGACOES_DDD_MOVEL - @VALOR_LIGACOES_DDD_MOVEL_ATUAL,
            VALOR_LIGACOES_LOCAL_MOVEL = VALOR_LIGACOES_LOCAL_MOVEL - @VALOR_LIGACOES_LOCAL_MOVEL_ATUAL,
            VALOR_LIGACOES_LOCAL_FIXO = VALOR_LIGACOES_LOCAL_FIXO - @VALOR_LIGACOES_LOCAL_FIXO_ATUAL,
            VALOR_LIGACOES_DDI = VALOR_LIGACOES_DDI - @VALOR_LIGACOES_DDI_ATUAL,
            VALOR_LIGACOES_VOIP = VALOR_LIGACOES_VOIP - @VALOR_LIGACOES_VOIP_ATUAL,
            VALOR_LIGACOES_TOM_REMOTO = VALOR_LIGACOES_TOM_REMOTO - @VALOR_LIGACOES_TOM_REMOTO_ATUAL
        WHERE ID_CONTRATACAO = @ID_CONTRATACAO_ATUAL AND ID_COMPETENCIA = @ID_COMPETENCIA
    END
    ELSE
    BEGIN
        UPDATE CAD_CUSTO_PRODUTO
        SET VALOR_LIGACOES_DDD_FIXO = VALOR_LIGACOES_DDD_FIXO + (@VALOR_LIGACOES_DDD_FIXO_NOVO - @VALOR_LIGACOES_DDD_FIXO_ATUAL),
            VALOR_LIGACOES_DDD_MOVEL = VALOR_LIGACOES_DDD_MOVEL + (@VALOR_LIGACOES_DDD_MOVEL_NOVO - @VALOR_LIGACOES_DDD_MOVEL_ATUAL),
            VALOR_LIGACOES_LOCAL_MOVEL = VALOR_LIGACOES_LOCAL_MOVEL + (@VALOR_LIGACOES_LOCAL_MOVEL_NOVO - @VALOR_LIGACOES_LOCAL_MOVEL_ATUAL),
            VALOR_LIGACOES_LOCAL_FIXO = VALOR_LIGACOES_LOCAL_FIXO + (@VALOR_LIGACOES_LOCAL_FIXO_NOVO - @VALOR_LIGACOES_LOCAL_FIXO_ATUAL),
            VALOR_LIGACOES_DDI = VALOR_LIGACOES_DDI + (@VALOR_LIGACOES_DDI_NOVO - @VALOR_LIGACOES_DDI_ATUAL),
            VALOR_LIGACOES_VOIP = VALOR_LIGACOES_VOIP + (@VALOR_LIGACOES_VOIP_NOVO - @VALOR_LIGACOES_VOIP_ATUAL),
            VALOR_LIGACOES_TOM_REMOTO = VALOR_LIGACOES_TOM_REMOTO + (@VALOR_LIGACOES_TOM_REMOTO_NOVO - @VALOR_LIGACOES_TOM_REMOTO_ATUAL)
        WHERE ID_CONTRATACAO = @ID_CONTRATACAO_ATUAL AND ID_COMPETENCIA = @ID_COMPETENCIA
    END
END
END

UPDATE CAD_BILHETES
SET ID_PREFIXO = I.ID_PREFIXO,
    ID_PRODUTO = I.ID_PRODUTO,  
    ID_COMPETENCIA = I.ID_COMPETENCIA,  
    DATA_HORA = I.DATA_HORA,
    DURACAO = I.DURACAO,
    TIPO = I.TIPO,
    NUMERO_EXTERNO = I.NUMERO_EXTERNO,
    RAMAL = I.RAMAL,
    SENHA_AUTORIZACAO = I.SENHA_AUTORIZACAO,
    CODIGO_DEPENDENCIA = I.CODIGO_DEPENDENCIA,
    CRITICA = I.CRITICA,
    [STATUS] = I.STATUS,
    VALOR = I.VALOR,
    SEQUENCIAL = I.SEQUENCIAL,
    FOI_TARIFADO = I.FOI_TARIFADO,
    FOI_CANCELADO = I.FOI_CANCELADO,
    DURACAO_TARIFACAO = I.DURACAO_TARIFACAO
FROM INSERTED AS I
WHERE CAD_BILHETES.ID = I.ID
END
Run Code Online (Sandbox Code Playgroud)

Jon*_*gel 7

SET @ID = (SELECT ID FROM INSERTED)
SET @ID_COMPETENCIA = (SELECT ID_COMPETENCIA FROM INSERTED)

这是一个常见的误解,即每行受影响触发一次,而实际上每个 DML 语句只触发一次触发器主体**。

在触发器主体中,表inserteddeleted表包含受操作影响的所有行。这样做是出于(至少)两个原因:

  1. 逻辑运算。如果触发器主体只有一行可用,则一组受影响的行中执行任务将非常痛苦。
  2. 表现。可以使用高效的基于集合的操作一次处理所有受影响的行。

单行触发器代码可能会在很长一段时间内不被注意,因为它可能合法地适用于您的用例,并且只有在有人决定影响多于一行时才会中断。更糟糕的是,触发代码实际上可能会成功,并最终做错事而没有错误

不幸的是,在编写触发器时没有安全措施可以告诉您此信息,因此您很幸运地发现了此错误!

**对于百姓心中与SQL Server 2008+读这篇文章,MERGE分别触发了3种不同的类型,因为它实际上只是一个事务“宏”干什么INSERT+ UPDATE+DELETE在相同的T-SQL语句。

  • @JonSeigel:这并不是真正的误解,它是其他 DBMS 系统(例如 Oracle、IBM DB2)中的一项功能。您可以选择触发触发器的内容(每行或完整语句)。SQL Server 没有它,所以习惯了其他技术的人很容易错过它。否则你的答案是值得的。 (4认同)