我需要在表A上编写一个Insert,Update触发器,它将删除表B中的所有行,其中一列(称为Desc)具有值,如插入/更新的值表A的列(比如Col1)。我将如何去写它,以便我可以处理更新和插入案例。我将如何确定是否为更新或插入执行触发器。插入更新触发器如何确定是否插入或更新
回答
如果是MS SQL服务器...
触发器具有特殊的INSERTED
和DELETED
表来跟踪“之前”和“之后”的数据。因此,您可以使用类似IF EXISTS (SELECT * FROM DELETED)
的内容来检测更新。更新时只有DELETED
中的行,但总是有INSERTED
中的行。
寻找 “插入” 在CREATE TRIGGER
编辑,2011年11月23日
评论后,这个答案只对INSERTED
和UPDATED
触发器。
显然,DELETE触发器不能有“在INSERTED
总是排”正如我上面
说了很多搜索我找不到它处理的所有(3)三个条件的单个SQL服务器触发的确切实例后触发器操作INSERT,UPDATE和DELETE。我终于找到了一段文字,谈到了发生DELETE或UPDATE时,常见的DELETED表将包含这两个动作的记录。根据这些信息,我创建了一个小型的Action例程,它确定了触发器被激活的原因。当在INSERT与UPDATE触发器上同时存在通用配置和特定操作时,有时需要此类接口。在这些情况下,为UPDATE创建单独的触发器,INSERT将成为维护问题。 (即两个触发器正确更新为必要的常见数据算法修复?)
为此,我想给出以下多触发事件代码片段来处理INSERT,UPDATE,DELETE在一个触发器中的Microsoft SQL Server。
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][email protected]_ID AND [LINE_NO][email protected]_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][email protected]_ID AND [LINE_NO][email protected]_NO
END
END
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
这可能是一个更快的方法:
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'
这种方式对于具有大量列的表无效,因为columns_updated()返回的是一个巨大的varbinary。因此,“> 0”失败,因为0默认为一个内部存储的数字,比从columns_updated()返回的值小得多() – Graham 2012-04-05 16:07:43
与所提供两种解决方案的一个潜在的问题是,这取决于它们是如何写的,更新查询可以更新零个记录和一个插入查询可能会插入零个记录。在这些情况下,插入和删除记录集将为空。在很多情况下,如果Inserted和Deleted记录集都是空的,你可能只想退出触发器而不做任何事情。
我发现了一个小错误,否则格雷厄姆凉爽的解决方案:
应该 IF COLUMNS_UPDATED()<> 0 - 插入或更新
代替> 0 可能是因为最高位被解释作为SIGNED整数符号位...(?)。 所以总:
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
很多的这些建议如果运行删除任何一个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了他们的答案。
我从他们的脚本构建了这个。
这是奖励,处理不存在的删除。干得好! – 2017-04-05 18:58:08
我们也可能有一个不影响行(甚至是INSERT)的UPDATE。 – 2017-08-31 10:06:57
在第一种情形下我假设你的表有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
快速解决方案的MySQL
通过方式:我使用MySQL PDO。
(1)在自动增量表刚刚从增量列每个脚本运行一次先拿到的最高值(我的列名= ID):
$select = "
SELECT MAX(id) AS maxid
FROM [tablename]
LIMIT 1
";
(2)运行MySQL查询你normaly会,并把结果为整数,如:
$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 @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])
只是简单的方式
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
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
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
我使用下面,还正确地检测删除删除任何声明:
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;
我我很长一段时间都在使用这些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
,看看是否列被更新,但它不是大数量的列表安全。我用了一些复杂的计算方法(请参阅下面的帮助文章)。此外,这种方法仍然会错误地将某些更新分类为插入(如果表中的每一列都受到更新的影响),并且可能会对只插入缺省值作为删除的插入进行分类,但这些插入是罕见的操作(在我的系统中是最初的)。 除此之外,目前我不知道如何改进此解决方案。
我喜欢的是解决方案, “计算机科学优雅。”我的解决方案在每次点击[插入]和[删除]伪类以获得它们的状态并将结果放入一个位映射变量中。然后,通过有效的二进制评估(除了不太可能的INSERT或DELETE组合),可以在整个触发器中轻松测试INSERT,UPDATE和DELETE的各种可能组合。
它确实假设如果没有行被修改(这应该满足绝大多数情况),它与DML语句的内容无关。所以尽管它不像罗马派克的解决方案那样完整,但它更有效率。使用这种方法,我们可以在每个表格中添加一个“FOR INSERT,UPDATE,DELETE”触发器,从而使我们能够:A)完全控制操作顺序,以及b)每个适用于多行为操作的代码实现。 (显然,每个实施模式都有其优缺点,您需要分别评估您的系统是否真正有效。)
请注意,由于没有磁盘访问(https://social.msdn.microsoft.com/Forums/en-US/01744422-23fe-42f6-9ab0-a255cdf2904a),“exists(select * from«inserted/deleted»)”语句非常有效。
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
相信嵌套IFS有点混乱和:
平优于嵌套[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
试试这个..
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
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
,同时我也很喜欢发表@Alex答案,我提供这个变化来@以上
格雷厄姆的解决这个专门使用在插入的记录存在与更新的表,而不是使用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. firebird - 插入或更新触发器后
- 2. 触发更新插入
- 3. 如何调试插入/更新触发器来查看插入/更新的值
- 4. PostgreSQL触发插入或更新
- 5. 触发MySql的插入或更新
- 6. 触发每次更新或插入
- 7. NHibernate如何确定是否插入或更新记录?
- 8. Oracle SQL触发器插入/更新
- 9. Oracle触发器插入/更新
- 10. 更新触发器插入为空
- 11. 插入,更新,删除的触发器
- 12. 更新插入MSSql触发器(Updatecounter)
- 13. T-SQL触发器插入更新
- 14. 插入更新后的SQL触发器
- 15. 插入触发器更新后
- 16. sql触发器更新并插入
- 17. Sql Server 2005 - 插入更新触发器 - 获取更新,插入行
- 18. 插入/更新/删除或单一触发器更好吗?
- 19. 插入或更新是否存在MYSQL
- 20. Postgres中的AFTER触发器是否会阻止插入/更新?
- 21. 使用触发器更新正在插入/更新的记录
- 22. 当更新或插入表中发生更新列值时创建触发器
- 23. SQL触发插入,删除,更新
- 24. 更新插入的行与触发
- 25. MySql触发更新,插入和删除
- 26. 更新操作触发插入查询?
- 27. MySQL插入后触发更新计数
- 28. SQL触发多个插入更新
- 29. 如何在更新触发之前更改插入触发器之前?
- 30. 当另一个表插入或更新时,Oracle触发器更新一个表
+1这比更有效`COUNT` – 2011-09-15 12:14:31
更有效的方法是“从插入选择1”... – ganders 2014-03-28 14:33:07
我喜欢写SELECT 1 FROM INSERTED,因为我认为它更清楚地表明意图,但如果这样我会被MSSQL程序员失望在这方面有所作为... – 2014-04-30 11:12:30