Откат транзакции от триггера
в MS SQL Server 2008 R2 нам нужен триггер предварительной вставки и предварительного обновления, который что-то проверяет и разрешает или откаты (через raiserror
) запущенная вставка / обновление.
вопрос: In INSTEAD OF
триггер. Действительно ли нужно явно писать insert или update? Потому что мы хотим, чтобы вставка или обновление по умолчанию были сделаны и делали только "precheck".
2 ответов
да.
вам нужно написать явное INSERT
или UPDATE
.
триггер работает INSTEAD OF
операция DML. Если вы оставите триггер пустым, то никаких действий не произойдет, кроме INSERTED
/ DELETED
таблицы создаются и заполняются в tempdb
.
хотя из обсуждения в комментариях я бы не использовал триггер для этого вообще, а использовал уникальный отфильтрованный индекс CREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> ''
. Это, вероятно, будет более эффективным и избежать потенциальной логики проблемы при работе с параллелизмом.
вы, вероятно, не хотите INSTEAD OF
триггер, если вы не хотите заменить фактическую вставку или обновление. В вашем случае, вы хотите FOR INSERT, UPDATE
триггер вместо этого.
этот триггер примера печатает сообщение клиенту, когда кто-либо пытается добавить или изменить данные в таблице titles.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE
AS RAISERROR ('inserts and updates to the titles table is not allowed', 16, 1)
GO
вы также можете использовать такие вещи, как IF EXISTS
или COLUMNS_UPDATED
как хорошо.
вот еще один пример, который использует откат.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'employee_insupd' AND type = 'TR')
DROP TRIGGER employee_insupd
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
@max_lvl tinyint,
@emp_lvl tinyint,
@job_id smallint
SELECT @min_lvl = min_lvl,
@max_lvl = max_lvl,
@emp_lvl = i.job_lvl,
@job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id
JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl <> 10)
BEGIN
RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
RAISERROR ('The level for job_id:%d should be between %d and %d.',
16, 1, @job_id, @min_lvl, @max_lvl)
ROLLBACK TRANSACTION
END
Я не уверен, есть ли у вас сделки или нет, но в вашем случае вы хотели бы что-то вроде следующего:
USE myDatabase
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'myTable' AND type = 'TR')
DROP TRIGGER tr_myTrigger
GO
CREATE TRIGGER tr_myTrigger
ON myTable
FOR INSERT, UPDATE
AS
if(exists(select * from inserted where rtrim(c) <> ''))
begin
-- check to make sure the insert(s) are unique
if(exists(
select * from inserted i
join dbo.myTable t on i.a = t.a and i.b = t.b and i.c = t.c)
begin
raiserror('Duplicate(s) found', 16, 1)
rollback transaction
end
end