Sql 多行更新触发器

Sql 多行更新触发器,sql,sql-server,tsql,sql-server-2008,triggers,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;

我的触发器有问题:

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
正在更新多行

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.
Msg 512,第16级,状态1,程序trg\U B\U\U登录,第83行
子查询返回了多个值。当子查询在=、!=、=或者当子查询用作表达式时。
声明已终止。
这只在我更新一行时起作用。
我该怎么做才能解决这个问题呢?

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

   SELECT @Old_DataRemocao=(SELECT * DataRemocao FROM deleted);
更新: 鉴于您最近的评论,我想补充一点,您需要尝试以下方法来审核登录表的更改:

insert into Login_Hs (columns)
select i.col1, i.col2, ..., i.colN
FROM inserted i
我看到两个问题

  • 您正试图将(Select*from)查询写入varchar变量。如果有人出现并向您的表中添加一列,则会中断。在选择框中指定您的列名

  • 如果代码一次只更新一行,那么触发器应该可以正常工作。如果更新次数超过此数,则插入和删除的表变量有多行。这就是错误。如果您在Oracle中工作,只需将FOR EACH ROW选项添加到触发器中,代码就会在更改的记录中进行光标移动

  • 在MSSQL中,如果确实需要执行此操作,则必须在“已删除”或“已插入”中的记录上打开光标,并逐个处理行。这可能真的很慢。如果我是你,我会尝试重构此代码以使用集合操作

    大概是这样的:

    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;
    
    感谢@Bill

    我做到了,而且成功了!ID、Login、PassWord、IDHomePage、IDSkin、IDPerfil、IDCriador、Email、DataCriacao、DataRemocao、Nome是我的表登录的字段,我保存它们

    插入登录页面(IDUtilizador、Tabela、Metodo、Chave、Campos、Data) 选择“1”、“登录”、“更新”、“ID=”+转换(nvarchar,i.ID), 'Login={'+Convert(nvarchar,coalesce(d.Login,'NULL'))+'->'+Convert(nvarchar,coalesce(i.Login,'NULL'))+'};'+ 'PassWord={'+coalesce(d.PassWord,'NULL')+'->'+coalesce(i.PassWord,'NULL')+'};'+ 'IDHomePage={'+Convert(nvarchar,coalesce(d.IDHomePage,'NULL'))+'->'+Convert(nvarchar,coalesce(i.IDHomePage,'NULL'))+'};'+ 'IDPerfil={'+Convert(nvarchar,coalesce(d.IDPerfil,'NULL'))+'->'+Convert(nvarchar,coalesce(i.IDPerfil,'NULL'))+'};'+ 'IDCriador={'+Convert(nvarchar,coalesce(d.IDCriador,'NULL'))+'->'+Convert(nvarchar,coalesce(i.IDCriador,'NULL'))+'};'+ 'Email={'+coalesce(d.Email,'NULL')+'->'+coalesce(i.Email,'NULL')+'};'+ 'DataCriacao={'+coalesce(Convert(varchar(20),d.DataCriacao,105),'NULL')+'->'+coalesce(Convert(varchar(20),i.DataCriacao,105),'NULL')+'};'+ 'DataRemocao={'+coalesce(Convert(varchar(20),d.DataRemocao,105),'NULL')+'->'+coalesce(Convert(varchar(20),i.DataRemocao,105),'NULL')+'};'+ 'Nome={'+coalesce(d.Nome,'NULL')+'->'+coalesce(i.Nome,'NULL')+'};', 转换(DATETIME,GETDATE(),105) 从插入i i.ID=d.ID上删除的内部联接d


    有没有办法只保存不同的行?

    忘记告诉我要将每行中所做的所有更改保存到另一个表中,即“基于集合的语句”,对吗?我搜索解决方案,我发现了,但我不知道如何使用它。因此,不要使用我的插入,对吗?还有一个问题,如果我只想保存更改的数据?@SlimBoy
    inserted
    是正在插入的新数据,
    deleted
    是旧数据,即被新数据替代;当然你可以加入他们,发现他们之间的差异,或者你能告诉我怎么做吗?我正在尝试这样做,如果(存在(选择*作为ins.ID=del.ID中ins.PassWorddel.PassWord作为ins插入的del-internal-JOIN)开始选择@Campos=@Campos+'PassWord={'+del.PassWord+'->'+ins.PassWord+'};'从deleted as del internal JOIN insert as ins ON ins.ID=del.ID END但它不起作用……我认为最好的做法是将已更改行的旧版本存储在历史记录表中,并在报表生成器中进行处理,或者创建xslt转换,该转换将完成清除这些数据并检测对这些数据的更改的工作输出到最终用户。
    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;