Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 在INSERT、UPDATE、DELETE之后触发调用具有表名和主键的存储过程_Sql Server_Triggers - Fatal编程技术网

Sql server 在INSERT、UPDATE、DELETE之后触发调用具有表名和主键的存储过程

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

对于同步进程,我的SQL Server数据库应该记录已更改的列表项—表名和主键

DB已经有一个表和存储过程来执行此操作:

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
进一步:除了解决你指定的问题,值得注意的是关于更大的图景的两点

  • 正如@Damien_的不信教者指出的那样,有内置的机制来完成变更跟踪,这将表现得更好
  • 如果您仍然希望处理自己的更改跟踪,那么如果您能够将其安排为一次性处理整个记录集,而不是执行逐行操作,那么它的性能会更好。有两种方法可以实现这一点a)将更改跟踪代码移到触发器中,不要使用SP。b)使用“用户定义的表类型”将更改记录集传递给SP

  • 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