답변:
트리거는 특별해야 INSERTED
하고 DELETED
데이터 "다음"과 "이전"추적 테이블을. 따라서 IF EXISTS (SELECT * FROM DELETED)
업데이트를 탐지하는 것과 같은 것을 사용할 수 있습니다 . DELETED
업데이트시 행만 있지만 항상 행이 있습니다.INSERTED
있습니다.
CREATE TRIGGER 에서 "inserted"를 찾으십시오 .
편집, 2011 년 11 월 23 일
주석 후이 답변은 오직 트리거 INSERTED
및 UPDATED
트리거에 대한 것입니다.
분명히, INSERTED
위에서 언급 한 것처럼 DELETE 트리거는 "항상 행"을 가질 수 없습니다.
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가 테이블에 존재하지 않는 값과 같은 곳을 삭제하려고한다고 가정 해보십시오.
트리거는 여전히 호출되지만 삭제 또는 삽입 된 테이블에는 아무 것도 없습니다.
이것을 안전하게 사용하십시오 :
--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 트리거 동작의 세 가지 조건을 모두 처리하는 단일 SQL Server 트리거의 정확한 예를 찾을 수 없었습니다. 마지막으로 DELETE 또는 UPDATE가 발생할 때 공통 DELETED 테이블에이 두 작업에 대한 레코드가 포함된다는 사실에 대해 설명하는 텍스트 줄을 찾았습니다. 그런 정보를 바탕으로 트리거가 활성화 된 이유를 결정하는 작은 동작 루틴을 만들었습니다. 이 유형의 인터페이스는 공통 구성과 INSERT vs. UPDATE 트리거에서 발생하는 특정 조치가있을 때 가끔 필요합니다. 이 경우 UPDATE 및 INSERT에 대해 별도의 트리거를 작성하면 유지 보수 문제가됩니다. (즉, 필요한 공통 데이터 알고리즘 수정을 위해 두 트리거가 올바르게 업데이트 되었습니까?)
이를 위해 Microsoft SQL Server에 대한 하나의 트리거에서 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
나는 중첩 된 ifs가 약간 혼란스럽고 믿습니다.
평지가 중첩 된 것보다 낫다 [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
@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
가 어디에서 문을 닫았
이것은 더 빠른 방법 일 수 있습니다.
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'
Grahams에서 멋진 해결책이 아니라면 작은 오류를 발견했습니다.
IF COLUMNS_UPDATED () < > 0-
최상위 비트가 SIGNED 정수 부호 비트 ... (?)로 해석되므로> 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 @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])
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
나는 "컴퓨터 과학 우아한"솔루션을 좋아합니다. 내 솔루션은 여기에서 각각 [inserted] 및 [deleted] 의사 테이블을 한 번 눌러 상태를 가져오고 결과를 비트 매핑 변수에 넣습니다. 그런 다음 INSERT, UPDATE 및 DELETE의 각 가능한 조합을 트리거 전체에서 효율적인 이진 평가 (쉽게 INSERT 또는 DELETE 조합 제외)로 쉽게 테스트 할 수 있습니다.
행이 수정되지 않은 경우 (대부분의 경우를 충족해야 함) DML 문의 내용이 중요하지 않다고 가정합니다. 따라서 Roman Pekar의 솔루션만큼 완벽하지는 않지만 더 효율적입니다.
이 접근 방식을 사용하면 테이블 당 하나의 "FOR INSERT, UPDATE, DELETE"트리거가 가능하므로 A) 동작 순서를 완벽하게 제어하고 b) 다중 동작 적용 가능한 동작마다 하나의 코드 구현을 제공 할 수 있습니다. (모든 구현 모델에는 장단점이 있으므로 시스템을 실제로 가장 잘 작동하는지 개별적으로 평가해야합니다.)
디스크에 액세스 할 수 없기 때문에 "존재 (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
빠른 솔루션 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
}
나는 그것이 빠르고 더럽다는 것을 안다. 그리고 그것은 오래된 게시물입니다. 그러나, 나는 오랫동안 해결책을 찾고 있었고 누군가 어쨌든 내 방식이 다소 유용하다는 것을 알 수 있습니다. 모두 제일 좋다!
간단한 방법
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
첫 번째 시나리오에서는 테이블에 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
이 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'
나는 이것을한다:
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)
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;