Триггер для обновления многострочного - PullRequest
2 голосов
/ 17 сентября 2010

У меня проблема с этим триггером:

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 ]

1 голос
/ 17 сентября 2010

Я вижу две проблемы.

  1. Вы пытаетесь записать запрос (Select * From) в переменную varchar. Это сломается, если кто-то придет и добавит столбец к вашей таблице. Укажите название вашего столбца в списке выбора.

  2. Ваш триггер должен работать нормально, если ваш код обновляет только одну строку за раз. Если вы обновите больше, переменные таблицы INSERTED и DELETED будут иметь несколько строк. Таким образом, ошибка. Если вы работаете в Oracle, вы можете просто добавить опцию FOR EACH ROW к своему триггеру, и код будет перемещаться по измененным записям.

В MSSQL, если вам действительно нужно это сделать, вам придется открыть курсор на записях в DELETED или INSERTED и обрабатывать строки по одной. Это может быть очень медленно. Если бы я был на вашем месте, я бы попытался изменить код для использования операций над множествами.

Примерно так:

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;
0 голосов
/ 17 сентября 2010

Спасибо @ Биллу,

Я сделал это и работает!Идентификатор, Логин, PassWord, IDHomePage, IDSkin, IDPerfil, IDCriador, Электронная почта, DataCriacao, DataRemocao, Nome - это поля моей таблицы Вход в систему, и я сохраняю их.Chave, Campos, Data) ВЫБЕРИТЕ «1», «Login», «UPDATE», «ID =» + convert (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') + '};', CONVERT (DATETIME, GETDATE (), 105) ИЗ вставленного i ВНУТРЕННЕЕ СОЕДИНЕНИЕ удалено d ON i.ID = d.ID;

Можно ли как-то просто сохранить те, которые отличаются?

0 голосов
/ 17 сентября 2010

Ну, вы пытаетесь поймать одно значение в потенциально многозначном запросе.Одним из таких мест является следующее:

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

Обновление: В свете ваших недавних замечаний я хотел бы добавить, что вам нужно попробовать что-то подобное для проверки изменений в имени пользователястол:

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