触发更新多行

时间:2010-09-17 15:29:04

标签: sql sql-server tsql sql-server-2008 triggers

我遇到了这个触发器的问题:

ALTER TRIGGER [dbo].[trg_B_U_Login]
   ON [dbo].[Login]
   FOR UPDATE
AS
BEGIN
    IF @@ROWCOUNT = 0
        RETURN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @Campos nvarchar(500);
    DECLARE @Old_DataRemocao nvarchar(20);
    DECLARE @New_DataRemocao nvarchar(20);
    DECLARE @Old_IDCriador nvarchar(10);
    DECLARE @New_IDCriador nvarchar(10);
    SELECT @Campos='';

    IF(Exists(SELECT * FROM deleted WHERE DataRemocao is NULL))
    BEGIN
        SELECT @Old_DataRemocao='NULL';
    END
    ELSE
    BEGIN
        SELECT @Old_DataRemocao=(SELECT * DataRemocao FROM deleted);
    END

    IF(Exists(SELECT * FROM inserted WHERE DataRemocao is NULL))
    BEGIN
        SELECT @New_DataRemocao=NULL;

    END
    ELSE
    BEGIN
        SELECT @New_DataRemocao=(SELECT * DataRemocao FROM inserted);
    END

    IF(@Old_DataRemocao<>@New_DataRemocao)
    BEGIN
        SELECT @Campos=@Campos+'DataRemocao={'+@Old_DataRemocao+' -> ' + @New_DataRemocao +'}; ';
    END

    IF(EXISTS(SELECT * FROM deleted as del, inserted AS ins WHERE ins.Login<>del.Login))
    BEGIN
        SELECT @Campos=@Campos+'Login={'+ del.Login +' -> '+ ins.Login+'}; ' FROM deleted as del, inserted as ins
    END

......

    IF(EXISTS(SELECT * FROM deleted as del, inserted AS ins WHERE ins.DataCriacao<>del.DataCriacao))
    BEGIN
        SELECT @Campos=@Campos+'DataCriacao={'+ del.DataCriacao +' -> '+ ins.DataCriacao+'}; ' FROM deleted as del, inserted as ins
    END

    IF(EXISTS(SELECT * FROM deleted as del, inserted AS ins WHERE ins.Nome<>del.Nome))
    BEGIN
        SELECT @Campos=@Campos+'Nome={'+ del.Nome +' -> '+ ins.Nome+'}; ' FROM deleted as del, inserted as ins
    END

    IF(@Campos<>'')
    BEGIN
        INSERT Login_Hs (IDUtilizador, Tabela, Metodo, Chave, Campos, Data)
            SELECT '1', 'Login', 'UPDATE', 'IDLogin='+Convert(nvarchar,ID),
                @Campos, CONVERT(DATETIME, GETDATE(), 105)
            FROM inserted
    END
END

保存在此更新中正在更新和执行的每一行中所做的更改

UPDATE Login
SET DataRemocao = CONVERT(datetime,GETDATE(),105)
WHERE IDPerfil = 25 AND DataRemocao IS NULL;

正在更新多行

和sql server 2008发出此错误:

Msg 512, Level 16, State 1, Procedure trg_B_U_Login, Line 83
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.

这在我更新单行时才有效。 我该怎么做才能解决这个问题?

3 个答案:

答案 0 :(得分:1)

我看到两个问题。

  1. 您正在尝试从(Select * From)查询写入varchar变量。如果有人出现并为您的表添加一列,那将会破坏。在select中指定列名。

  2. 如果您的代码一次只更新一行,那么您的触发器应该可以正常工作。如果更新的次数超过,则INSERTED和DELETED表变量有多行。因此错误。如果您在Oracle中工作,只需在触发器中添加FOR EACH ROW选项,代码就会光标处理已更改的记录。

  3. 在MSSQL中,如果您确实需要这样做,则必须在DELETED或INSERTED中的记录上打开游标并逐个处理这些行。这可能非常慢。如果我是你,我会尝试重构此代码以使用set操作。

    这样的事情:

    ALTER TRIGGER [dbo].[trg_B_U_Login]
       ON [dbo].[Login]
       FOR UPDATE
    AS
    BEGIN
            INSERT Login_Hs (IDUtilizador, Tabela, Metodo, Chave, Campos, Data)
                SELECT '1', 'Login', 'UPDATE', 'IDLogin='+Convert(nvarchar,ID),
                'DataRemocao={'+ coalesce(deleted.campos, 'NULL') + ' -> ' 
                   + coalesce(inserted.campos, 'NULL'),
                   CONVERT(DATETIME, GETDATE(), 105)
                FROM inserted
                  Inner Join deleted on inserted.idcol=deleted.idcol
    END;
    

答案 1 :(得分:0)

嗯,您正试图在潜在的多值查询中捕获单个值。其中一个地方如下:

   SELECT @Old_DataRemocao=(SELECT * DataRemocao FROM deleted);

<强>更新 根据您最近的评论,我想补充一点,您需要尝试这样的方法来审核登录表的更改:

insert into Login_Hs (columns)
select i.col1, i.col2, ..., i.colN
FROM inserted i

答案 2 :(得分:0)

感谢@Bill,

我这样做并且有效! ID,登录,密码,IDHomePage,IDSkin,IDPerfil,IDCriador,电子邮件,DataCriacao,DataRemocao,Nome是我的表登录的字段,我保存它们。

INSERT INTO Login_Hs(IDUtilizador,Tabela,Metodo,Chave,Campos,Data)         SELECT'1','Login','UPDATE','ID ='+ convert(nvarchar,i.ID),             'Login = {'+ Convert(nvarchar,coalesce(d.Login,'NULL'))+' - &gt; '+转换(nvarchar,coalesce(i.Login,'NULL'))+'};' +             'PassWord = {'+ coalesce(d.PassWord,'NULL')+' - &gt; '+ coalesce(i.PassWord,'NULL')+'};' +             'IDHomePage = {'+ Convert(nvarchar,coalesce(d.IDHomePage,'NULL'))+' - &gt; '+ Convert(nvarchar,coalesce(i.IDHomePage,'NULL'))+'};' +             'IDPerfil = {'+ Convert(nvarchar,coalesce(d.IDPerfil,'NULL'))+' - &gt; '+转换(nvarchar,coalesce(i.IDPerfil,'NULL'))+'};' +             'IDCriador = {'+ Convert(nvarchar,coalesce(d.IDCriador,'NULL'))+' - &gt; '+转换(nvarchar,coalesce(i.IDCriador,'NULL'))+'};' +             'Email = {'+ coalesce(d.Email,'NULL')+' - &gt; '+ coalesce(i.Email,'NULL')+'};' +             'DataCriacao = {'+ coalesce(Convert(varchar(20),d.DataCriacao,105),'NULL')+' - &gt; '+ coalesce(Convert(varchar(20),i.DataCriacao,105),'NULL')+'};' +             'DataRemocao = {'+ coalesce(Convert(varchar(20),d.DataRemocao,105),'NULL')+' - &gt; '+ coalesce(Convert(varchar(20),i.DataRemocao,105),'NULL')+'};' +             'Nome = {'+ coalesce(d.Nome,'NULL')+' - &gt; '+ coalesce(i.Nome,'NULL')+'};' ,             CONVERT(DATETIME,GETDATE(),105)         FROM插入i             INNER JOIN已删除d ON i.ID = d.ID;

有没有办法只保存不同的那些?