Как я могу заставить триггер срабатывать для каждой вставленной строки во время INSERT INTO Table (etc) SELECT * FROM Table2?

Я пытался избежать использования курсора в этом конкретном случае только потому, что мне не нравятся компромиссы, и так получилось, что процесс, который я использую, в любом случае делает триггеры похожими на правильный курс действий.

Хранимая процедура вставляет запись на основе сложного сочетания предложений, используя триггер вставки, я отправляю электронное письмо целевому пользователю, предлагая ему посетить сайт. Это легко и работает нормально.

Однако другая процедура заключается в том, чтобы запускать каждую ночь и перераспределять все непросмотренные записи. То, как я это делал, заключалось в том, чтобы сделать еще одну вставку на основе выбора поля даты, когда оно было назначено. А именно:

INSERT INTO Table (ID, User, AssignDate, LastActionDate)
    SELECT 
        ID
        ,User
        ,GETDATE() [AssignDate]
        ,GETDATE() [LastModifiedDate]
    FROM Table2
        /*snip*/

Триггер работает с отдельными вставками, но приведенный выше оператор select работает только с последней вставленной строкой. Есть ли способ обойти это поведение? Это портит все дело!

Изменить (код триггера):

ALTER TRIGGER dbo.Notify
    ON  dbo.Table
    AFTER INSERT
AS 
BEGIN

    DECLARE @EmailSender varchar(50)='Sender Profile'
    DECLARE @Identity int
    DECLARE @User varchar(20)
    DECLARE @Subject varchar(50)

    SET @Identity=@@Identity

    SELECT @User=User, @Subject='(' + CONVERT(varchar,@Identity) + ')!'
    FROM Table
    WHERE
        idNum=@Identity

    exec msdb.dbo.sp_send_dbmail
        @profile_name=@EmailSender,
        @recipients=@User
        @subject=@Subject,
        @body='//etc'

END

person C Bauer    schedule 27.04.2011    source источник
comment
Триггеры в SQL Server срабатывают один раз для инструкции, а не один раз для строки. Поэтому вам нужно написать свой код триггера, чтобы справиться с несколькими строками, существующими в inserted и/или deleted. Обычно это можно сделать, не прибегая к курсорам. Но нам нужно увидеть больше кода, чтобы посоветовать - все, что вы показали на данный момент, это стандартный оператор вставки.   -  person Damien_The_Unbeliever    schedule 27.04.2011
comment
Я думаю, вам понадобится курсор для этого. Я не думаю, что есть что-то, что позволяет вам вставлять несколько писем за один раз.   -  person Martin Smith    schedule 27.04.2011
comment
Вы бы не использовали @@identity, вы использовали поле id из вставленной псевдотаблицы. На самом деле вы почти никогда не должны использовать @@identity в каком-либо процессе, так как это может вернуть изношенные результаты и нарушить целостность наших данных.   -  person HLGEM    schedule 27.04.2011
comment
@C Bauer - Итак, если 100 строк вставлены в одно выражение, вы хотите отправить 100 электронных писем? Мне жаль таких получателей.   -  person Thomas    schedule 27.04.2011
comment
@Thomas - система является саморегулируемой, чтобы не назначать никого, кто получил уведомление за последние 30 дней, она переходит к следующему квалифицированному лицу для этого. Как только кто-то взаимодействует с веб-сайтом, эта конкретная запись никогда не «переходит» к следующему человеку.   -  person C Bauer    schedule 27.04.2011
comment
@C Бауэр - Интересно. Так что же произойдет в случае вставки 100 строк? Он проходит через подходящих получателей, а затем пропускает остальные электронные письма?   -  person Thomas    schedule 27.04.2011
comment
@Thomas - Вставки создаются представителями компании. Перераспределение происходит каждую ночь. В случае большого потока другой администратор вручную определяет, кто получает эти сообщения с помощью CMS. Они получают только одно электронное письмо с уведомлением об ожидающих ручных корректировках.   -  person C Bauer    schedule 27.04.2011


Ответы (2)


Триггер вставки вызывается один раз для массовых вставок, но в триггере вы можете использовать специальную таблицу inserted для получения всех вставленных строк.

Итак, представьте, что у вас есть триггер вставки, подобный этому, который регистрирует все строки, вставленные в table.

create trigger trgInsertTable 
on dbo.table
for insert
as
   insert tableLog(name)
    select name from inserted

С помощью этого триггера, когда вы выполняете массовую вставку в table, tableLog заполняется тем же количеством строк, которое было вставлено в table.

Для вашего конкретного триггера, поскольку вам нужно вызывать хранимую процедуру для каждой строки, вам нужно использовать курсор:

ALTER TRIGGER dbo.Notify
    ON  dbo.Table
    AFTER INSERT
AS 
BEGIN

    DECLARE @EmailSender varchar(50)='Sender Profile'
    DECLARE @User varchar(20)
    DECLARE @Subject varchar(50)

    DECLARE cursor CURSOR FOR
      SELECT User, '(' + CONVERT(varchar, Id) + ')!'
        FROM inserted

    OPEN cursor
    FETCH NEXT FROM cursor INTO @User, @Subject
    WHILE @@FETCH_STATUS = 0
    BEGIN
      exec msdb.dbo.sp_send_dbmail
          @profile_name=@EmailSender,
          @recipients=@User
          @subject=@Subject,
          @body='//etc'
      FETCH NEXT FROM cursor INTO @User, @Subject
    END
    CLOSE cursor
    DEALLOCATE cursor

END

я не проверял, но должно работать

person Jose Rui Santos    schedule 27.04.2011
comment
Я полагаю, что тогда мне следует просто пропустить триггер и переместить код электронной почты обратно в хранимую процедуру, которая обрабатывает все как есть? - person C Bauer; 27.04.2011
comment
Просто очень незначительный комментарий - в случае нечувствительного экземпляра вам нужно будет выбрать другое имя для курсора, чем «курсор». - person Jan; 12.12.2013

Если вы отправляете электронное письмо, я бы не стал делать это из триггера. Вы действительно хотите, чтобы люди не могли вставлять записи, потому что сервер электронной почты не работает?

Обычно лучше вставлять записи в таблицу из триггера, а затем иметь задание, которое отправляет электронные письма, которое запускается каждую минуту или около того, обновляет статус электронной почты на отправленное и добавляет отправленную дату и время в таблицу при отправке каждой записи. Это не только позволяет вам вставлять записи, когда электронные письма не работают, но и перемещает цикл для отправки каждого отдельного электронного письма в таблицу, к которой пользователи не обращаются (и, таким образом, любая задержка обработки многих записей повлияет только на новых пользователей, а не на кого-либо еще), и это позволяет вам увидеть историю отправки электронного письма, что помогает, когда люди задаются вопросом, почему они не получили его. Вы также можете записать в таблицу, если электронное письмо не удалось отправить, чтобы помочь определить неверные адреса электронной почты.

person HLGEM    schedule 27.04.2011
comment
Запускать курсор каждую минуту? кажется некрасивым! - person C Bauer; 27.04.2011
comment
Довольно сложно отправлять электронные письма только одному человеку без курсора или цикла. по крайней мере, если они находятся в другой таблице, они не будут мешать нормальной работе. - person HLGEM; 27.04.2011
comment
@C Bauer,@HLGEM - это предпочтительнее запуска курсора в триггере. Кроме того, он позволяет отключать уведомления на машинах разработки, просто записывая в таблицу уведомлений, но отключая процесс отправки электронной почты. Этот подход также позволит вам экстернализовать отправку электронной почты в службу, если это необходимо. - person Thomas; 27.04.2011