Sql server 在INSERT、UPDATE、DELETE之后触发调用具有表名和主键的存储过程
对于同步进程,我的SQL Server数据库应该记录已更改的列表项—表名和主键 DB已经有一个表和存储过程来执行此操作:Sql server 在INSERT、UPDATE、DELETE之后触发调用具有表名和主键的存储过程,sql-server,triggers,Sql Server,Triggers,对于同步进程,我的SQL Server数据库应该记录已更改的列表项—表名和主键 DB已经有一个表和存储过程来执行此操作: EXEC @ErrCode = dbo.SyncQueueItem "tableName", 1234; 我想向表中添加触发器,以便在插入、更新、删除时调用此存储过程。我怎么才能拿到钥匙?可能有效的最简单的方法是什么 CREATE TABLE new_employees ( id_num INT IDENTITY(1,1), fname VARCHAR(20
EXEC @ErrCode = dbo.SyncQueueItem "tableName", 1234;
我想向表中添加触发器,以便在插入、更新、删除时调用此存储过程。我怎么才能拿到钥匙?可能有效的最简单的方法是什么
CREATE TABLE new_employees
(
id_num INT IDENTITY(1,1),
fname VARCHAR(20),
minit CHAR(1),
lname VARCHAR(30)
);
GO
IF OBJECT_ID ('dbo.sync_new_employees','TR') IS NOT NULL
DROP TRIGGER sync_new_employees;
GO
CREATE TRIGGER sync_new_employees
ON new_employees
AFTER INSERT, UPDATE, DELETE
AS
DECLARE @Key Int;
DECLARE @ErrCode Int;
-- How to get the key???
SELECT @Key = 12345;
EXEC @ErrCode = dbo.SyncQueueItem "new_employees", @key;
GO
不是最好的解决方案,只是对问题的直接回答:
SELECT @Key = COALESCE(deleted.id_num,inserted.id_num);
同样不是最好的方法(如果不是最坏的话)(不要在家里尝试),但至少它对多个值有帮助:
DECLARE @Key INT;
DECLARE triggerCursor CURSOR LOCAL FAST_FORWARD READ_ONLY
FOR SELECT COALESCE(i.id_num,d.id_num) AS [id_num]
FROM inserted i
FULL JOIN deleted d ON d.id_num = i.id_num
WHERE (
COALESCE(i.fname,'')<>COALESCE(d.fname,'')
OR COALESCE(i.minit,'')<>COALESCE(d.minit,'')
OR COALESCE(i.lname,'')<>COALESCE(d.lname,'')
)
;
OPEN triggerCursor;
FETCH NEXT FROM triggerCursor INTO @Key;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
FETCH NEXT FROM triggerCursor INTO @Key;
END
CLOSE triggerCursor;
DEALLOCATE triggerCursor;
DECLARE@Key INT;
声明触发器游标本地快进只读
对于选择合并(i.id\u num,d.id\u num)作为[id\u num]
从插入i
完全联接已在d.id\u num=i.id\u num上删除d
在哪里(
合并(i.fname,)合并(d.fname,)
或聚结(i.minit,)聚结(d.minit,)
或联合(i.lname,)联合(d.lname,)
)
;
打开触发器光标;
从triggerCursor取下一个到@Key;
而@@FETCH\u STATUS=0
开始
EXEC@ErrCode=dbo.SyncQueueItem'new_employees',@key;
从triggerCursor取下一个到@Key;
结束
关闭触发光标;
释放触发器游标;
使用基于触发器的“值更改跟踪器”的更好方法:
插入[YourTableHistoryName](id_num,fname,minit,lname,何时出现)
选择COALESCE(i.id\u num,d.id\u num)作为[id\u num]
,i.fname,i.minit,i.lname,当前时间戳为[出现时]
从插入i
完全联接已在d.id\u num=i.id\u num上删除d
其中(COALESCE(i.fname.))COALESCE(d.fname.))
或聚结(i.minit,)聚结(d.minit,)
或联合(i.lname,)联合(d.lname,)
)
;
在我看来,跟踪更改的最佳方法是使用时态表(SQL Server 2016+)您应该使用魔术表来获取数据。 通常,插入和删除的表在触发器的上下文中称为魔法表。SQL Server中有插入和删除的魔法表。这些表由SQL Server在内部自动创建和管理,以便在数据库表的DML操作(插入、更新和删除)期间保存最近插入、删除和更新的值 插入的魔法表 插入的表保存最近插入的值,即新的数据值。因此,最近添加的记录被插入到插入的表中 删除的魔法表 删除的表保存最近删除或更新的值,换句话说,旧数据值。因此,旧的更新和删除记录将插入到已删除的表中 **您可以使用插入和删除的魔法表来获取id_num的值**
SELECT top 1 @Key = id_num from inserted
注意:对于插入场景,此代码示例仅适用于单个记录。对于大容量插入/更新方案,您需要从临时表或变量中存储的已插入和已删除的表中获取记录,然后循环通过该表传递给您的过程,或者您可以将表变量传递给您的过程并在其中处理多条记录 访问操作更改的记录的方法是使用SQL Server提供的
插入的
和删除的
伪表
Inserted
包含任何插入的记录,或任何具有新值的更新记录
Deleted
包含任何已删除的记录,或任何具有旧值的更新记录
在编写触发器时,为了安全起见,应该始终为多个记录被操作的情况编写代码。不幸的是,如果你需要调用一个SP,这意味着一个循环——这并不理想
下面的代码显示了如何为您的示例执行此操作,并包括一种检测操作是否为插入/更新/删除的方法
declare @Key int, @ErrCode int, @Action varchar(6);
declare @Keys table (id int, [Action] varchar(6));
insert into @Keys (id, [Action])
select coalesce(I.id, D.id_num)
, case when I.id is not null and D.id is not null then 'Update' when I.id is not null then 'Insert' else 'Delete' end
from Inserted I
full join Deleted D on I.id_num = D.id_num;
while exists (select 1 from @Keys) begin
select top 1 @Key = id, @Action = [Action] from @Keys;
exec @ErrCode = dbo.SyncQueueItem 'new_employees', @key;
delete from @Keys where id = @Key;
end
进一步:除了解决你指定的问题,值得注意的是关于更大的图景的两点
DML触发器应操作设置数据,否则仅处理一行数据。可以是这样的。当然,还要使用魔术表
插入
和删除
CREATE TRIGGER dbo.tr_employees
ON dbo.employees --the table from Northwind database
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare @tbl table (id int identity(1,1),delId int,insId int)
--Use "magic tables" inserted and deleted
insert @tbl(delId, insId)
select d.EmployeeID, i.EmployeeID
from inserted i --empty when "delete"
full join deleted d --empty when "insert"
on i.EmployeeID=d.EmployeeID
declare @id int,@key int,@action char
select top 1 @id=id, @key=isnull(delId, insId),
@action=case
when delId is null then 'I'
when insId is null then 'D'
else 'U' end --just in case you need the operation executed
from @tbl
--do something for each row
while @id is not null --instead of cursor
begin
--do the main action
--exec dbo.sync 'employees', @key, @action
--remove processed row
delete @tbl where id=@id
--refill @variables
select top 1 @id=id, @key=isnull(delId, insId),
@action=case
when delId is null then 'I'
when insId is null then 'D'
else 'U' end --just in case you need the operation executed
from @tbl
end
END
在触发器中插入/删除将生成与所触摸的行数相同的行数,每键调用存储的过程将需要每行使用游标或类似的方法。 您应该检查SQL Server中的时间戳/行版本。您可以将其添加到有问题的所有表中(非空、自动递增、每个表/行在数据库中唯一等)。 可以将该列上的唯一索引添加到添加该列的所有表中。 @@DBTS是当前的时间戳,您可以存储今天的@@DBTS,明天您将扫描从该时间戳到当前@@DBTS的所有表。对于所有更新和插入,时间戳/行版本将增加,但对于删除,它不会跟踪,对于删除,您可以使用仅删除触发器,并将键插入到其他表中。 更改数据捕获或更改跟踪可以更轻松地做到这一点,但如果服务器上有大量的数据或大量的数据加载,分区交换机扫描事务日志将成为瓶颈,在某些情况下,您必须删除更改数据捕获以保存事务日志,以免事务日志不稳定地增长。我想您是这样的
CREATE TRIGGER dbo.tr_employees
ON dbo.employees --the table from Northwind database
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare @tbl table (id int identity(1,1),delId int,insId int)
--Use "magic tables" inserted and deleted
insert @tbl(delId, insId)
select d.EmployeeID, i.EmployeeID
from inserted i --empty when "delete"
full join deleted d --empty when "insert"
on i.EmployeeID=d.EmployeeID
declare @id int,@key int,@action char
select top 1 @id=id, @key=isnull(delId, insId),
@action=case
when delId is null then 'I'
when insId is null then 'D'
else 'U' end --just in case you need the operation executed
from @tbl
--do something for each row
while @id is not null --instead of cursor
begin
--do the main action
--exec dbo.sync 'employees', @key, @action
--remove processed row
delete @tbl where id=@id
--refill @variables
select top 1 @id=id, @key=isnull(delId, insId),
@action=case
when delId is null then 'I'
when insId is null then 'D'
else 'U' end --just in case you need the operation executed
from @tbl
end
END