Триггер Вставить обновление, как определить, вставить или обновить - PullRequest
145 голосов
/ 12 апреля 2009

Мне нужно написать триггер вставки и обновления для таблицы A, который удалит все строки из таблицы B, один столбец (скажем, Desc) имеет значения, подобные значению, вставленному / обновленному в столбце таблицы A (например, Col1). Как бы мне написать его так, чтобы я мог обрабатывать как обновления, так и вставки. Как определить, выполняется ли триггер для обновления или вставки.

Ответы [ 20 ]

2 голосов
/ 21 марта 2012

Это может быть более быстрый способ:

DECLARE @action char(1)

IF COLUMNS_UPDATED() > 0 -- insert or update
BEGIN
    IF EXISTS (SELECT * FROM DELETED) -- update
        SET @action = 'U'
    ELSE
        SET @action = 'I'
    END
ELSE -- delete
    SET @action = 'D'
2 голосов
/ 02 марта 2018

, хотя мне также нравится ответ, опубликованный @Alex, я предлагаю этот вариант решения @ Грэма выше

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

declare @action varchar(4)
    IF EXISTS (SELECT * FROM INSERTED)
        BEGIN
            IF EXISTS (SELECT * FROM DELETED) 
                SET @action = 'U'  -- update
            ELSE
                SET @action = 'I'  --insert
        END
    ELSE IF EXISTS (SELECT * FROM DELETED)
        SET @action = 'D'  -- delete
    else 
        set @action = 'noop' --no records affected
--print @action

вы получите NOOP со следующим утверждением:

update tbl1 set col1='cat' where 1=2
1 голос
/ 29 января 2014

Это помогает мне:

declare @action_type int;
select @action_type = case
                       when i.id is not null and d.id is     null then 1 -- insert
                       when i.id is not null and d.id is not null then 2 -- update
                       when i.id is     null and d.id is not null then 3 -- delete
                     end
  from      inserted i
  full join deleted  d on d.id = i.id

Поскольку не все столбцы могут быть обновлены одновременно, вы можете проверить, обновляется ли определенный столбец чем-то вроде этого:

IF UPDATE([column_name])
1 голос
/ 06 июля 2016

Мне нравятся решения, которые "элегантны в компьютерных науках". Мое решение здесь обращается к псевдотаблям [вставлено] и [удалено] один раз, чтобы получить их статусы, и помещает результат в битовую переменную. Тогда каждая возможная комбинация INSERT, UPDATE и DELETE может быть легко протестирована в течение всего триггера с эффективными двоичными оценками (за исключением маловероятной комбинации INSERT или DELETE).

Предполагается, что не имеет значения, каким был оператор DML, если строки не были изменены (что должно удовлетворять подавляющему большинству случаев). Так что, хотя оно не такое полное, как решение Романа Пекара, оно более эффективно.

При таком подходе у нас есть возможность одного триггера «FOR INSERT, UPDATE, DELETE» на таблицу, что дает нам A) полный контроль над порядком действий и b) одну реализацию кода на действие, применимое к нескольким действиям. (Очевидно, что каждая модель реализации имеет свои плюсы и минусы; вам нужно будет оценивать свои системы индивидуально на предмет того, что действительно работает лучше всего.)

Обратите внимание, что операторы «существует (выберите * из« вставлено / удалено »)» очень эффективны, поскольку нет доступа к диску (https://social.msdn.microsoft.com/Forums/en-US/01744422-23fe-42f6-9ab0-a255cdf2904a).

use tempdb
;
create table dbo.TrigAction (asdf int)
;
GO
create trigger dbo.TrigActionTrig
on dbo.TrigAction
for INSERT, UPDATE, DELETE
as
declare @Action tinyint
;
-- Create bit map in @Action using bitwise OR "|"
set @Action = (-- 1: INSERT, 2: DELETE, 3: UPDATE, 0: No Rows Modified 
  (select case when exists (select * from inserted) then 1 else 0 end)
| (select case when exists (select * from deleted ) then 2 else 0 end))
;
-- 21 <- Binary bit values
-- 00 -> No Rows Modified
-- 01 -> INSERT -- INSERT and UPDATE have the 1 bit set
-- 11 -> UPDATE <
-- 10 -> DELETE -- DELETE and UPDATE have the 2 bit set

raiserror(N'@Action = %d', 10, 1, @Action) with nowait
;
if (@Action = 0) raiserror(N'No Data Modified.', 10, 1) with nowait
;
-- do things for INSERT only
if (@Action = 1) raiserror(N'Only for INSERT.', 10, 1) with nowait
;
-- do things for UPDATE only
if (@Action = 3) raiserror(N'Only for UPDATE.', 10, 1) with nowait
;
-- do things for DELETE only
if (@Action = 2) raiserror(N'Only for DELETE.', 10, 1) with nowait
;
-- do things for INSERT or UPDATE
if (@Action & 1 = 1) raiserror(N'For INSERT or UPDATE.', 10, 1) with nowait
;
-- do things for UPDATE or DELETE
if (@Action & 2 = 2) raiserror(N'For UPDATE or DELETE.', 10, 1) with nowait
;
-- do things for INSERT or DELETE (unlikely)
if (@Action in (1,2)) raiserror(N'For INSERT or DELETE.', 10, 1) with nowait
-- if already "return" on @Action = 0, then use @Action < 3 for INSERT or DELETE
;
GO

set nocount on;

raiserror(N'
INSERT 0...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 0 object_id from sys.objects;

raiserror(N'
INSERT 3...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 3 object_id from sys.objects;

raiserror(N'
UPDATE 0...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t where asdf <> asdf;

raiserror(N'
UPDATE 3...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t;

raiserror(N'
DELETE 0...', 10, 1) with nowait;
delete t from dbo.TrigAction t where asdf < 0;

raiserror(N'
DELETE 3...', 10, 1) with nowait;
delete t from dbo.TrigAction t;
GO

drop table dbo.TrigAction
;
GO
1 голос
/ 09 сентября 2014
declare @insCount int
declare @delCount int
declare @action char(1)

select @insCount = count(*) from INSERTED
select @delCount = count(*) from DELETED

    if(@insCount > 0 or @delCount > 0)--if something was actually affected, otherwise do nothing
    Begin
        if(@insCount = @delCount)
            set @action = 'U'--is update
        else if(@insCount > 0)
            set @action = 'I' --is insert
        else
            set @action = 'D' --is delete

        --do stuff here
    End
0 голосов
/ 07 февраля 2014

просто, просто

CREATE TRIGGER [dbo].[WO_EXECUTION_TRIU_RECORD] ON [dbo].[WO_EXECUTION]
WITH EXECUTE AS CALLER
FOR INSERT, UPDATE
AS
BEGIN  

  select @vars = [column] from inserted 
  IF UPDATE([column]) BEGIN
    -- do update action base on @vars 
  END ELSE BEGIN
    -- do insert action base on @vars 
  END

END 
0 голосов
/ 20 февраля 2013

В первом сценарии я предположил, что в вашей таблице есть столбец IDENTITY

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10)
SELECT @action = CASE WHEN COUNT(i.Id) > COUNT(d.Id) THEN 'inserted'
                      WHEN COUNT(i.Id) < COUNT(d.Id) THEN 'deleted' ELSE 'updated' END
FROM inserted i FULL JOIN deleted d ON i.Id = d.Id

Во втором сценарии не нужно использовать столбец IDENTITTY

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10),
        @insCount int = (SELECT COUNT(*) FROM inserted),
        @delCount int = (SELECT COUNT(*) FROM deleted)
SELECT @action = CASE WHEN @insCount > @delCount THEN 'inserted'
                      WHEN @insCount < @delCount THEN 'deleted' ELSE 'updated' END
0 голосов
/ 13 марта 2013

Быстрое решение MySQL

Кстати: я использую MySQL PDO.

(1) В таблице автоинкремента просто получите самое высокое значение (имя моего столбца = id) из увеличенного столбца при первом запуске каждого сценария:

$select = "
    SELECT  MAX(id) AS maxid
    FROM    [tablename]
    LIMIT   1
";

(2) Запустите запрос MySQL, как обычно, и приведите результат к целому числу, например ::

$iMaxId = (int) $result[0]->maxid;

(3) После запроса «INSERT INTO ... ON DUPLICATE KEY UPDATE» получите последний вставленный идентификатор вашего предпочтительного способа, например ::

$iLastInsertId = (int) $db->lastInsertId();

(4) Сравнить и отреагировать: если lastInsertId выше самого высокого в таблице, это, вероятно, INSERT, верно? И наоборот.

if ($iLastInsertId > $iMaxObjektId) {
    // IT'S AN INSERT
}
else {
    // IT'S AN UPDATE
}

Я знаю, что это быстро и, возможно, грязно. И это старый пост. Но, эй, я долго искал решение, и, может быть, кто-то найдет мой путь в любом случае полезным. Всего наилучшего!

0 голосов
/ 21 декабря 2015

Я давно использовал эти exists (select * from inserted/deleted) запросы, но этого все же недостаточно для пустых операций CRUD (когда в таблицах inserted и deleted нет записей). Поэтому, немного изучив эту тему, я нашел более точное решение:

declare
    @columns_count int = ?? -- number of columns in the table,
    @columns_updated_count int = 0

-- this is kind of long way to get number of actually updated columns
-- from columns_updated() mask, it's better to create helper table
-- or at least function in the real system
with cte_columns as (
    select @columns_count as n
    union all
    select n - 1 from cte_columns where n > 1
), cte_bitmasks as (
    select
        n,
        (n - 1) / 8 + 1 as byte_number,
        power(2, (n - 1) % 8) as bit_mask
    from cte_columns
)
select
    @columns_updated_count = count(*)
from cte_bitmasks as c
where
    convert(varbinary(1), substring(@columns_updated_mask, c.byte_number, 1)) & c.bit_mask > 0

-- actual check
if exists (select * from inserted)
    if exists (select * from deleted)
        select @operation = 'U'
    else
        select @operation = 'I'
else if exists (select * from deleted)
    select @operation = 'D'
else if @columns_updated_count = @columns_count
    select @operation = 'I'
else if @columns_updated_count > 0
    select @operation = 'U'
else
    select @operation = 'D'

Также можно использовать columns_updated() & power(2, column_id - 1) > 0, чтобы увидеть, обновляется ли столбец, но это небезопасно для таблиц с большим количеством столбцов. Я использовал немного сложный способ вычисления (см. Полезную статью ниже).

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

0 голосов
/ 13 августа 2014
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

SELECT @INSERTEDCOUNT = COUNT([YourColumnName]) FROM inserted

SELECT @DELETEDCOUNT = COUNT([YourColumnName]) FROM deleted

ЕСЛИ его обновление

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

если его вставка

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