挿入更新トリガー挿入または更新かどうかを判断する方法


162

テーブルAに挿入、更新トリガーを記述する必要があります。これにより、テーブルAの列(たとえば、Col1)に挿入/更新された値のような値が1つの列(たとえば、Desc)にあるテーブルBからすべての行が削除されます。UpdateとInsertの両方のケースを処理できるようにするにはどうすればよいでしょうか。トリガーが更新または挿入のために実行されるかどうかをどのように判断しますか?

回答:


167

トリガーにはINSERTEDDELETED「前」と「後」のデータを追跡するための特別なテーブルがあります。したがってIF EXISTS (SELECT * FROM DELETED)、更新を検出するようなものを使用できます。DELETED更新時にのみ行がありますが、常に行がありますINSERTED

CREATE TRIGGERで「inserted」を探しますます。

編集、2011年11月23日

コメントの後、この回答はトリガーINSERTEDUPDATEDトリガーのみです。
明らかに、DELETEトリガーは、「常に行を持つことはできませんINSERTED、私は上記の言ったように」


完全な回答については、以下の@MikeTeeVeeの回答をご覧ください。これは不完全です。
Lorenz Meyer

1
@LorenzMeyerの元の質問はそれを必要としません。EXISTS(SELECT * FROM DELETED)もあります。なぜそれが完全ではないと思うのかわからない...
gbn

@LorenzMeyerが参照している可能性があるのは、「更新時にはDELETEDにのみ行がありますが、常に INSERTEDに行があります。」というステートメントです。更新/挿入トリガーが呼び出され、INSERTEDが呼び出される場合があるため、これは必ずしも当てはまりません空の。私の回答では、述語がデータの変更を排除することによってこれがどのように引き起こされるかを説明します。このイベントでは、DMLの試行に対してトリガーが呼び出されますが、DELETEDテーブルとINSERTEDテーブルは空です。これは、SQLが(データが変更されない場合でも)各DMLの試行をログに記録したい場合も考慮しているためです。
MikeTeeVee

127
CREATE TRIGGER dbo.TableName_IUD
ON dbo.TableName
AFTER INSERT, UPDATE, DELETE
AS 
BEGIN
    SET NOCOUNT ON;

    --
    -- Check if this is an INSERT, UPDATE or DELETE Action.
    -- 
    DECLARE @action as char(1);

    SET @action = 'I'; -- Set Action to Insert by default.
    IF EXISTS(SELECT * FROM DELETED)
    BEGIN
        SET @action = 
            CASE
                WHEN EXISTS(SELECT * FROM INSERTED) THEN 'U' -- Set Action to Updated.
                ELSE 'D' -- Set Action to Deleted.       
            END
    END
    ELSE 
        IF NOT EXISTS(SELECT * FROM INSERTED) RETURN; -- Nothing updated or inserted.

    ...

    END

1
私はそれがより明確に意図信号化だと思うあまりにとして挿入から選択した1を書きたいが、これは...このコンテキストで任意の違いがあれば、私は、MSSQLプログラマによってdisapointedされるだろう
ルカシュLánskýを

26
IF EXISTS(SELECT * ...)とIF EXISTS(SELECT 1)...のパフォーマンスはまったく同じです。行はまったく読み取られず、フェッチもされません。実際、IF EXISTS(SELECT 1/0 ...)を使用することもできますが、それでも機能し、ゼロ除算エラーは発生しません。
Endrju 2014年

1
挿入、更新、削除用に別々のトリガーを作成していました。これらを組み合わせることができることを知ったのは素晴らしいことです。
UJSは

2
誰かが2つの異なる行をINSERTおよびDELETEするクエリを書き込んだ場合(同じスクリプトで新しい行を挿入して別の行を削除する)、上記の方法で設定されたトリガーが実際にそれをUPDATEとして識別できる可能性があります(インテントINSERTED / DELETED SQLテーブルにデータがあるため、実際には更新ではありませんか?
mche

87

何も削除しないdeleteステートメントを実行した場合、これらの提案の多くは考慮されません。
IDがテーブルに存在しない値と等しい場所を削除しようとするとします。
トリガーは引き続き呼び出されますが、削除または挿入されたテーブルには何もありません。

安全のためにこれを使用してください:

--Determine if this is an INSERT,UPDATE, or DELETE Action or a "failed delete".
DECLARE @Action as char(1);
    SET @Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
                         AND EXISTS(SELECT * FROM DELETED)
                        THEN 'U'  -- Set Action to Updated.
                        WHEN EXISTS(SELECT * FROM INSERTED)
                        THEN 'I'  -- Set Action to Insert.
                        WHEN EXISTS(SELECT * FROM DELETED)
                        THEN 'D'  -- Set Action to Deleted.
                        ELSE NULL -- Skip. It may have been a "failed delete".   
                    END)

@KenDogと@Net_Progの回答に特に感謝します。
私は彼らのスクリプトからこれを構築しました。


3
これは賞品で、存在しない削除を扱います。よくできました!
Andrew Wolfe

6
行に影響を及ぼさないUPDATE(またはINSERT)もあるかもしれません。
Razvan Socol

@AndrewWolfe?何言ってるの?質問には、「テーブルAに挿入、更新トリガーを記述する必要がある」と具体的に記載されています。DELETEトリガーについては何もしません。
ypercubeᵀᴹ

@ypercubeᵀᴹ申し訳ありませんが、私のトリガーの約80%が3つのタイミングすべてをカバーしています。
Andrew Wolfe

18

私は以下を使用しています、何も削除しない削除ステートメントも正しく検出します:

CREATE TRIGGER dbo.TR_TableName_TriggerName
    ON dbo.TableName
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS(SELECT * FROM INSERTED)
        -- DELETE
        PRINT 'DELETE';
    ELSE
    BEGIN
        IF NOT EXISTS(SELECT * FROM DELETED)
            -- INSERT
            PRINT 'INSERT';
        ELSE
            -- UPDATE
            PRINT 'UPDATE';
    END
END;

4
しかし、これは何も挿入も更新もしないステートメントを誤って検出します。
Roman Pekar

11

多くの検索の後、トリガーアクションINSERT、UPDATE、およびDELETEの3つすべての条件を処理する単一のSQL Serverトリガーの正確な例を見つけることができませんでした。最後に、DELETEまたはUPDATEが発生すると、共通のDELETEDテーブルにこれら2つのアクションのレコードが含まれるという事実を説明する1行のテキストが見つかりました。その情報に基づいて、トリガーがアクティブ化された理由を判別する小さなアクションルーチンを作成しました。このタイプのインターフェースは、INSERTトリガーとUPDATEトリガーで発生する共通の構成と特定のアクションの両方がある場合に必要になることがあります。これらのケースでは、UPDATEとINSERTの個別のトリガーを作成することは、メンテナンスの問題になります。(つまり、両方のトリガーは、必要な共通データアルゴリズムの修正のために適切に更新されましたか?)

そのために、Microsoft SQL Serverの1つのトリガーでINSERT、UPDATE、DELETEを処理するための次のマルチトリガーイベントコードスニペットを提供します。

CREATE TRIGGER [dbo].[INSUPDDEL_MyDataTable]
ON [dbo].[MyDataTable] FOR INSERT, UPDATE, DELETE
AS 

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with caller queries SELECT statements.
-- If an update/insert/delete occurs on the main table, the number of records affected
-- should only be based on that table and not what records the triggers may/may not
-- select.
SET NOCOUNT ON;

--
-- Variables Needed for this Trigger
-- 
DECLARE @PACKLIST_ID varchar(15)
DECLARE @LINE_NO smallint
DECLARE @SHIPPED_QTY decimal(14,4)
DECLARE @CUST_ORDER_ID varchar(15)
--
-- Determine if this is an INSERT,UPDATE, or DELETE Action
-- 
DECLARE @Action as char(1)
DECLARE @Count as int
SET @Action = 'I' -- Set Action to 'I'nsert by default.
SELECT @Count = COUNT(*) FROM DELETED
if @Count > 0
    BEGIN
        SET @Action = 'D' -- Set Action to 'D'eleted.
        SELECT @Count = COUNT(*) FROM INSERTED
        IF @Count > 0
            SET @Action = 'U' -- Set Action to 'U'pdated.
    END

if @Action = 'D'
    -- This is a DELETE Record Action
    --
    BEGIN
        SELECT @PACKLIST_ID =[PACKLIST_ID]
                    ,@LINE_NO = [LINE_NO]
        FROM DELETED

        DELETE [dbo].[MyDataTable]
        WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
    END
 Else
    BEGIN
            --
            -- Table INSERTED is common to both the INSERT, UPDATE trigger
            --
            SELECT @PACKLIST_ID =[PACKLIST_ID]
                ,@LINE_NO = [LINE_NO]
                ,@SHIPPED_QTY =[SHIPPED_QTY]
                ,@CUST_ORDER_ID = [CUST_ORDER_ID]
            FROM INSERTED 

         if @Action = 'I'
            -- This is an Insert Record Action
            --
            BEGIN
                INSERT INTO [MyChildTable]
                    (([PACKLIST_ID]
                    ,[LINE_NO]
                    ,[STATUS]
                VALUES
                    (@PACKLIST_ID
                    ,@LINE_NO
                    ,'New Record'
                    )
            END
        else
            -- This is an Update Record Action
            --
            BEGIN
                UPDATE [MyChildTable]
                    SET [PACKLIST_ID] = @PACKLIST_ID
                          ,[LINE_NO] = @LINE_NO
                          ,[STATUS]='Update Record'
                WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
            END
    END   

9

ネストされたifsは少し混乱していると思います:

フラットはネストよりも優れている[Zen of Python]

;)

DROP TRIGGER IF EXISTS AFTER_MYTABLE

GO

CREATE TRIGGER dbo.AFTER_MYTABLE ON dbo.MYTABLE AFTER INSERT, UPDATE, DELETE 

AS BEGIN 

    --- FILL THE BEGIN/END SECTION FOR YOUR NEEDS.

    SET NOCOUNT ON;

    IF EXISTS(SELECT * FROM INSERTED)  AND EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'UPDATE' END 
    ELSE IF EXISTS(SELECT * FROM INSERTED)  AND NOT EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'INSERT' END 
    ELSE IF    EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
        BEGIN PRINT 'DELETED' END
    ELSE BEGIN PRINT 'NOTHING CHANGED'; RETURN; END  -- NOTHING

END

9
Declare @Type varchar(50)='';
IF EXISTS (SELECT * FROM inserted) and  EXISTS (SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'UPDATE'
END
ELSE IF EXISTS(SELECT * FROM inserted)
BEGIN
    SELECT @Type = 'INSERT'
END
ElSE IF EXISTS(SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'DELETE'
END

5

これを試して..

ALTER TRIGGER ImportacionesGS ON dbo.Compra 
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
  -- idCompra is PK
  DECLARE @vIdCompra_Ins INT,@vIdCompra_Del INT
  SELECT @vIdCompra_Ins=Inserted.idCompra FROM Inserted
  SELECT @vIdCompra_Del=Deleted.idCompra FROM Deleted
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NULL)  
  Begin
     -- Todo Insert
  End
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Update
  End
  IF (@vIdCompra_Ins IS NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Delete
  End
END

4

@Alexが投稿した回答も気に入っていますが、上記の@Grahamのソリューションにこのバリエーションを提供しています

これは、最初のテストでCOLUMNS_UPDATEDを使用するのではなく、INSERTEDテーブルと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

最初ENDが間違ってインデントされているようです!(最初BEGINが閉じている場所を質問する)
S.Serpooshan 2018

else ifおよびfinal elseには単一のステートメントが含まれます。IF / Elseは単一のステートメントであるため、開始と終了は実際には不要です。インデントを修正しました。助けてくれてありがとう。
グレッグ

3

これはより速い方法かもしれません:

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'

4
この方法は、columns_updated()が巨大なvarbinaryを返すため、列数が多いテーブルでは機能しません。したがって、0はデフォルトで、columns_updated()から返される値よりもはるかに小さい、内部に格納された数になるため、 "> 0"は失敗します
Graham

3

提供されている2つのソリューションの潜在的な問題は、それらの記述方法によっては、更新クエリがゼロのレコードを更新し、挿入クエリがゼロのレコードを挿入する場合があることです。これらの場合、挿入および削除されたレコードセットは空になります。多くの場合、挿入されたレコードセットと削除されたレコードセットの両方が空の場合は、何もせずにトリガーを終了するだけです。


2

私はグラハムでそれ以外のクールな解決策で小さなエラーを見つけました:

IF COLUMNS_UPDATED()< > 0-> 0の
代わりに挿入または更新する必要があります。おそらく、トップビットが符号付き整数符号ビットとして解釈されるためです... 合計で:

DECLARE @action CHAR(8)  
IF COLUMNS_UPDATED() <> 0 -- delete or update?
BEGIN     
  IF EXISTS (SELECT * FROM deleted) -- updated cols + old rows means action=update       
    SET @action = 'UPDATE'     
  ELSE
    SET @action = 'INSERT' -- updated columns and nothing deleted means action=insert
END 
ELSE -- delete     
BEGIN
  SET @action = 'DELETE'
END

1

これは私にとってはトリックです:

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])

このソリューションの課題は、列名を知っている必要があることです。他の一部は、スニペットライブラリからコピーして貼り付けることができるように設計されています。小さな点ですが、すべてのことを考慮すると、一般的なソリューションはケース固有のソリューションよりも優れています。私見では。
集約:2018年

1
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

1
パフォーマンス上の理由から、COUNT(*)は使用しません。テーブル全体をスキャンする必要があります。代わりに、DELETEと同じようにIF EXISTS(SELECT * FROM INSERTED)を使用してフラグを設定します。通常、影響を受ける行は数行しかないことはわかっていますが、なぜシステムの速度が低下するのでしょうか。
Endrju 2014年

ソリューションとして非常によく似たものを投稿しようとしていました。それは少し冗長ですが、非常に読みやすいです。公正なトレードオフ。上記のGrahms Solutionも気に入っています。
集約:2018年

1

「コンピューターサイエンスのエレガント」なソリューションが好きです。ここでの私の解決策は、[挿入された]および[削除された]疑似テーブルを1回ずつヒットしてステータスを取得し、結果をビットマップ変数に入れます。次に、INSERT、UPDATE、およびDELETEの考えられる各組み合わせを、効率的なバイナリ評価を使用して、トリガー全体で容易にテストできます(INSERTまたはDELETEのありそうもない組み合わせを除く)。

変更された行がない場合は、DMLステートメントが何であるかは問題ではない(大多数のケースを満たす必要がある)と想定しています。したがって、Roman Pekarのソリューションほど完全ではありませんが、より効率的です。

このアプローチでは、テーブルごとに1つの「FOR INSERT、UPDATE、DELETE」トリガーの可能性があり、A)アクションの順序を完全に制御し、b)マルチアクションに適用可能なアクションごとに1つのコードを実装できます。(明らかに、すべての実装モデルには長所と短所があります。実際に最も効果的に機能するものについて、システムを個別に評価する必要があります。)

「exists(select * from«inserted / deleted»)」ステートメントは、ディスクアクセスがないため、非常に効率的です(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

私のコンテキストに足を踏み入れるこのソリューションをありがとう。更新/挿入された行のLastUpdated列を更新する方法をお勧めしますか?削除された行のID(構成されたキーである可能性があります)を別のテーブルに格納する方法もお勧めですか?
セバスチャン2017

0

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"クエリの後、最後に挿入されたIDを好みの方法で取得します。例:

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

(4)比較して反応する:lastInsertIdがテーブルの最高値よりも高い場合、それはおそらくINSERTでしょう。およびその逆。

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

私はそれが速くて多分汚れていることを知っています。そしてそれは古い記事です。しかし、ねえ、私は長い間解決策を探していました、そしておそらく誰かが私の方法がとにかくいくらか役立つと思います。ではごきげんよう!


0

単純な方法

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 

私のSSMS IDEによると、構文はIF BEGIN-END ELSE BEGIN-ENDブロックでロジックをラップする方法では正しくありません。
Erutan409 2015

0

最初のシナリオでは、テーブルに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

2番目のシナリオでは、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

同じ問題があり、誰かが私を助けることができます。以下のリンクを参照してくださいstackoverflow.com/questions/26043106/...
ラメシュS

0
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

0

私はそれらを使用しました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
declare @result as smallint
declare @delete as smallint = 2
declare @insert as smallint = 4
declare @update as smallint = 6
SELECT @result = POWER(2*(SELECT count(*) from deleted),1) + POWER(2*(SELECT 
     count(*) from inserted),2)

if (@result & @update = @update) 
BEGIN
  print 'update'
  SET @result=0
END
if (@result & @delete = @delete)
  print 'delete'
if (@result & @insert = @insert)
  print 'insert'

0

私はこれをします:

select isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)

1->挿入

2->削除

3->更新

set @i = isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)
--select @i

declare @action varchar(1) = case @i when 1 then 'I' when 2 then 'D' when 3 then 'U' end
--select @action


select @action c1,* from inserted t1 where @i in (1,3) union all
select @action c1,* from deleted t1 where @i in (2)

0
DECLARE @ActionType CHAR(6);
SELECT  @ActionType = COALESCE(CASE WHEN EXISTS(SELECT * FROM INSERTED)
                                     AND EXISTS(SELECT * FROM DELETED)  THEN 'UPDATE' END,
                               CASE WHEN EXISTS(SELECT * FROM DELETED)  THEN 'DELETE' END,
                               CASE WHEN EXISTS(SELECT * FROM INSERTED) THEN 'INSERT' END);
PRINT   @ActionType;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.