Как эмулировать триггер BEFORE DELETE в SQL Server 2005
Допустим, у меня есть три таблицы: [ONE], [ONE_TWO] и [TWO]. [ONE_TWO] - это таблица соединений "многие ко многим", содержащая только столбцы [ONE_ID и [TWO_ID]. Существуют внешние ключи, настроенные для связи [ONE] с [ONE_TWO] и [TWO] с [ONE_TWO]. FK используют опцию ON DELETE CASCADE, чтобы при удалении записи [ONE] или [TWO] также автоматически удалялись связанные записи [ONE_TWO].
Я хочу иметь триггер для таблицы [TWO], чтобы при удалении записи [TWO] она выполняла хранимую процедуру, которая принимает в качестве параметра [ONE_ID], передавая значения [ONE_ID], которые были связаны с [ TWO_ID] до удаления:
DECLARE @Statement NVARCHAR(max)
SET @Statement = ''
SELECT @Statement = @Statement + N'EXEC [MyProc] ''' + CAST([one_two].[one_id] AS VARCHAR(36)) + '''; '
FROM deleted
JOIN [one_two] ON deleted.[two_id] = [one_two].[two_id]
EXEC (@Statement)
Ясно, что мне нужен триггер BEFORE DELETE, но в SQL Server 2005 такого нет. Я не могу использовать триггер INSTEAD OF из-за каскадного FK.
У меня складывается впечатление, что если я использую триггер FOR DELETE, когда я присоединяю [удалено] к [ONE_TWO], чтобы найти список значений [ONE_ID], каскад FK уже удалит соответствующие записи [ONE_TWO], поэтому я никогда не буду найти любые значения [ONE_ID]. Это правда? Если так, как я могу достичь своей цели?
Я думаю, что мне нужно изменить FK, соединяющий [TWO] на [ONE_TWO], чтобы не использовать каскады и вручную выполнять удаление из [ONE_TWO] в триггере непосредственно перед тем, как я вручную удаляю записи [TWO]. Но я бы предпочел не проходить через все это, если есть более простой способ.
1 ответ
Вы можете использовать триггер INSTEAD OF. Он запускается до (заменяет) фактического удаления, поэтому связанная запись в [one_two] должна все еще существовать.
create table [one] (one_id int not null primary key)
create table [two] (two_id int not null primary key)
create table [one_two] (one_id int, two_id int references two(two_id) on delete cascade)
GO
CREATE trigger t_del_two
on two
instead of delete
as
begin
SET NOCOUNT ON
DECLARE @Statement NVARCHAR(max)
SET @Statement = ''
SELECT @Statement = @Statement + N'EXEC [MyProc] ''' + CAST([one_two].[one_id] AS VARCHAR(36)) + '''; '
FROM deleted
JOIN [one_two] ON deleted.[two_id] = [one_two].[two_id]
PRINT (@Statement)
--EXEC (@Statement)
-- carry out the actual delete
DELETE TWO WHERE two_id in (SELECT two_id from deleted)
end
GO
Некоторые примеры значений
insert into one select 1
insert into one select 2
insert into one select 3
insert into two select 11
insert into two select 12
insert into two select 13
insert into one_two select 1,11
insert into one_two select 1,13
insert into one_two select 2,13
Теперь проверь это
delete two where two_id=13