Триггер AFTER INSERT, UPDATE, DELETE для вызова хранимой процедуры с именем таблицы и первичным ключом - PullRequest
3 голосов
/ 07 марта 2019

Для процесса синхронизации моя база данных SQL Server должна записывать измененные элементы списка - имя таблицы и первичный ключ.

В БД уже есть таблица и хранимая процедура для этого:

EXEC @ErrCode = dbo.SyncQueueItem "tableName", 1234;

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

CREATE TABLE new_employees
(
    id_num INT IDENTITY(1,1),
    fname VARCHAR(20),
    minit CHAR(1),
    lname VARCHAR(30)
);
GO

IF OBJECT_ID ('dbo.sync_new_employees','TR') IS NOT NULL 
    DROP TRIGGER sync_new_employees;
GO

CREATE TRIGGER sync_new_employees
ON new_employees
AFTER INSERT, UPDATE, DELETE
AS
     DECLARE @Key Int;
     DECLARE @ErrCode Int;

     --  How to get the key???
     SELECT @Key = 12345; 

     EXEC @ErrCode = dbo.SyncQueueItem "new_employees", @key;
GO

Ответы [ 4 ]

4 голосов
/ 07 марта 2019

Чтобы получить доступ к записям, измененным операцией, используйте псевдотаблицы Inserted и Deleted, предоставленные вам SQL Server.

Inserted содержит любые вставленные записи или любые обновленные записи с их новыми значениями.

Deleted содержит все удаленные записи или любые обновленные записи со своими старыми значениями.

Подробнее

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

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

declare @Key int, @ErrCode int, @Action varchar(6);

declare @Keys table (id int, [Action] varchar(6));

insert into @Keys (id, [Action])
  select coalesce(I.id, D.id_num)
    , case when I.id is not null and D.id is not null then 'Update' when I.id is not null then 'Insert' else 'Delete' end
  from Inserted I
  full join Deleted D on I.id_num = D.id_num;

while exists (select 1 from @Keys) begin
  select top 1 @Key = id, @Action = [Action] from @Keys;
  exec @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
  delete from @Keys where id = @Key;
end

Далее: Помимо решения указанной проблемы стоит отметить пару моментов, касающихся более широкой картины.

  1. Как указывает @Damien_The_Unbeliever, есть встроенные механизмы для отслеживания изменений, которые будут работать намного лучше.
  2. Если вы все еще хотите обрабатывать собственное отслеживание изменений, было бы лучше, если бы вы организовали его так, чтобы обрабатывать весь набор записей за один раз, а не выполнять построчную операцию. Есть 2 способа сделать это: а) Поместите свой код отслеживания изменений в триггер и не используйте SP. б) Используйте «Определенный пользователем тип таблицы», чтобы передать набор записей изменений в SP.
1 голос
/ 07 марта 2019

Триггер DML должен работать установить данные, иначе будет обработана только одна строка. Это может быть что-то вроде этого. И конечно же используйте магические столы inserted и deleted.

CREATE TRIGGER dbo.tr_employees 
   ON  dbo.employees --the table from Northwind database
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
        declare @tbl table (id int identity(1,1),delId int,insId int)

        --Use "magic tables" inserted and deleted
        insert @tbl(delId, insId)
        select d.EmployeeID, i.EmployeeID
        from inserted i --empty when "delete"
        full join deleted d --empty when "insert"
            on i.EmployeeID=d.EmployeeID

        declare @id int,@key int,@action char

        select top 1 @id=id, @key=isnull(delId, insId),
            @action=case 
                when delId is null then 'I' 
                when insId is null then 'D' 
                else 'U' end --just in case you need the operation executed
        from @tbl
        --do something for each row
        while @id is not null --instead of cursor
        begin
            --do the main action
            --exec dbo.sync 'employees', @key, @action
            --remove processed row
            delete @tbl where id=@id
            --refill @variables
            select top 1 @id=id, @key=isnull(delId, insId),
                @action=case 
                    when delId is null then 'I' 
                    when insId is null then 'D' 
                    else 'U' end --just in case you need the operation executed
            from @tbl
        end
END
1 голос
/ 07 марта 2019

Вы должны использовать Magic Table для получения данных.Обычно вставленные и удаленные таблицы называются Magic Tables в контексте триггера.В SQL Server есть вставленные и удаленные магические таблицы.Эти таблицы автоматически создаются и управляются внутренним сервером SQL Server для хранения недавно вставленных, удаленных и обновленных значений во время операций DML (вставка, обновление и удаление) в таблице базы данных.

Вставленная волшебная таблица

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

Удаленная волшебная таблица

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

** Вы можете использовать вставленную и удаленную волшебную таблицу, чтобы получить значение id_num **

 SELECT top 1 @Key = id_num from inserted  

Примечание:Этот пример кода будет работать только для одной записи для сценария вставки.Для массовых сценариев вставки / обновления вам нужно извлечь записи из вставленной и удаленной таблицы, хранящейся во временной таблице или переменной, а затем выполнить цикл по ней, чтобы перейти к вашей процедуре, или вы можете передать переменную таблицы своей процедуре и обработать несколько записей там.

0 голосов
/ 07 марта 2019

Не лучшее решение, а просто прямой ответ на вопрос :

SELECT @Key = COALESCE(deleted.id_num,inserted.id_num);

Также не лучшим образом (если не худшим) (не пытайтесь повторить это дома), но по крайней мере это поможет с несколькими значениями :

DECLARE @Key INT;
DECLARE triggerCursor CURSOR LOCAL FAST_FORWARD READ_ONLY 
    FOR SELECT COALESCE(i.id_num,d.id_num) AS [id_num]
        FROM inserted i
        FULL JOIN deleted d ON d.id_num = i.id_num
        WHERE (
                COALESCE(i.fname,'')<>COALESCE(d.fname,'')
             OR COALESCE(i.minit,'')<>COALESCE(d.minit,'')
             OR COALESCE(i.lname,'')<>COALESCE(d.lname,'')
        )
;

OPEN triggerCursor;
FETCH NEXT FROM triggerCursor INTO @Key;
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
    FETCH NEXT FROM triggerCursor INTO @Key;
END

CLOSE triggerCursor;
DEALLOCATE triggerCursor;

Лучший способ использовать отслеживатель изменения значения на основе триггера:

INSERT INTO [YourTableHistoryName] (id_num, fname, minit, lname, WhenHappened)
SELECT COALESCE(i.id_num,d.id_num) AS [id_num]
    ,i.fname,i.minit,i.lname,CURRENT_TIMESTAMP AS [WhenHeppened]
FROM inserted i
FULL JOIN deleted d ON d.id_num = i.id_num
WHERE ( COALESCE(i.fname,'')<>COALESCE(d.fname,'')
    OR COALESCE(i.minit,'')<>COALESCE(d.minit,'')
    OR COALESCE(i.lname,'')<>COALESCE(d.lname,'')
)
;

Лучший (на мой взгляд) способ отслеживать изменения - это использовать временные таблицы (SQL Server 2016 +)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...