テーブルAにInsert、Update Triggerを書く必要があります。これは、テーブルBの1つのカラム(Descなど)に、テーブルAのカラムに挿入/更新された値(Col1など)の値がある行をすべて削除します。更新と挿入の両方のケースを処理できるようにするには、どのようにしてそれを書きますか。更新または挿入に対してトリガーが実行されたかどうかをどのように判断しますか。
MS SQL Serverの場合.
トリガーには、 "前"と "後"のデータを追跡するための特別なINSERTED
テーブルとDELETED
テーブルがあります。そのため、IF EXISTS (SELECT * FROM DELETED)
のようなものを使って更新を検出することができます。更新時にはDELETED
にのみ行がありますが、INSERTED
には常に行があります。
CREATE TRIGGER の中で "挿入された"を探してください
編集、2011年11月23日
コメント終了後、この回答はINSERTED
およびUPDATED
トリガーに対してのみ行われます。
明らかに、上で述べたように、DELETEトリガーは "INSERTED
内の常に行"を持つことはできません。
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
何も削除しないdeleteステートメントを実行した場合、これらの提案の多くは考慮に入れられません。
IDがテーブルに存在しない値と等しい場所を削除しようとしたとします。
あなたのトリガーはまだ呼ばれていますが、DeletedまたはInsertedテーブルには何もありません。
安全のためにこれを使ってください。
--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に感謝します。
私はこれを彼らのスクリプトから作りました。
私は以下を使用しています、それはまた何も削除しないdeleteステートメントを正しく検出します。
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;
多くの検索を行った後、トリガーアクションINSERT、UPDATE、およびDELETEの3つすべての条件を処理する単一のSQL Serverトリガーの正確な例は見つかりませんでした。私は、DELETEまたはUPDATEが発生したときに、共通のDELETEDテーブルにこれら2つのアクションのレコードが含まれるという事実について話した一連のテキストを見つけました。その情報に基づいて、トリガーがアクティブになった理由を特定するための小さなActionルーチンを作成しました。このタイプのインターフェースは、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
入れ子になったifが少し混乱していると思います。
フラットはネストよりも優れています[The 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
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
これを試して..
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
提供されている2つの解決策の潜在的な問題は、それらがどのように書かれているかに応じて、更新クエリがゼロレコードを更新し、挿入クエリがゼロレコードを挿入する可能性があることです。これらの場合に挿入と削除レコードセットは、空になります。多くの場合、InsertedレコードセットとDeletedレコードセットの両方が空の場合は、何もせずに単にトリガを終了することをお勧めします。
これはより速い方法かもしれません:
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'
私は@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
私はGrahamsに小さなエラーを見つけました。
IF COLUMNS_UPDATED()<> 0 - 挿入または更新
トップビットがSIGNED integer sign bit ...(?)として解釈されるため、> 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
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
これは私にとってトリックです:
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])
私は「コンピュータサイエンス優雅」な解決策が好きです。ここでの私の解決策は、それぞれ[ステータス]を取得するために[挿入された]および[削除された]疑似テーブルにヒットし、その結果をビットマップ変数に格納します。その後、INSERT、UPDATE、DELETEのそれぞれの可能な組み合わせを、効率的なバイナリ評価を使用してトリガー全体で簡単にテストできます(ありそうもないINSERTまたはDELETEの組み合わせを除く)。
行が変更されなかった場合(大多数のケースを満たす必要がある場合)、DML文がどのようなものであっても構わないという前提はありません。したがって、Roman Pekarのソリューションほど完全ではありませんが、より効率的です。
このアプローチでは、テーブルごとに1つの "FOR INSERT、UPDATE、DELETE"トリガが可能になり、A)アクションの順序を完全に制御し、b)マルチアクションに対応したアクションごとに1つのコードを実装できます。 (明らかに、すべての実装モデルには長所と短所があります。実際に最もうまく機能するかどうかについて、システムを個別に評価する必要があります。)
ディスクアクセスがないため、 "exists(select * from"挿入/削除")"ステートメントは非常に効率的です( 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
最初のシナリオでは、私はあなたのテーブルに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
クイックソリューションMySQL
ところで:私はMySQL PDOを使っています。
(1)自動インクリメントテーブルでは、スクリプトが最初に実行されるたびに、インクリメントされた列から最大値(my column name = id)を取得するだけです。
$select = "
SELECT MAX(id) AS maxid
FROM [tablename]
LIMIT 1
";
(2)通常どおりMySQLクエリを実行し、結果をintegerにキャストします。
$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
}
私はそれが迅速で多分汚いことを知っています。そしてそれは古い記事です。しかし、ねえ、私は長い間解決策を探していました、そして、多分誰かが私の方法がとにかくやや役立つと思うかもしれません。ではごきげんよう!
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
私は長い間これらの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
を使用することも可能ですが、列数が多いテーブルには安全ではありません。私は少し複雑な計算方法を使いました(下記の役に立つ記事を見てください)。
また、このアプローチではまだ一部の更新を挿入として誤って分類し(テーブル内のすべての列が更新の影響を受ける場合)、デフォルト値のみが削除として挿入されている挿入を分類します。私のシステムでリースしている)それ以外にも、現時点でこのソリューションを改善する方法がわかりません。
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'
簡単な方法
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