1、背景

环境:发布服务器A Windows2008+SQL2008,分发服务器B Windows2008+SQL2008,订阅服务器C Windows2008+SQL2012
发布服务器A上的用户信息表member,通过事务复制,推送到订阅器C上的用户信息表member。订阅服务器C上存在一张用户账户表,之前的机制是通过在用户信息表上的insert触发器往用户账户表初始化数据。
问题:发布服务器上的用户信息表的某一字段与订阅服务器上的用户信息表的某一字段内容不一致,近一个月出现了两次,运营反馈这个问题已经持续很长一段时间,希望我们能彻底解决,而不是每次用户反馈了再查再同步!
处理:主库导一份用户数据到订阅数据库上,对比不一致的字段有多少记录,无意中发现订阅的数据量少于发布的记录数。问题重心由字段不一致转移到数量不一致,是什么原因导致记录条数都不一样呢?启动复制监视器,查看分发到订阅的历史记录,发现下面的错误信息,查看这些时间点主库有记录插入,但订阅上找不到对应记录。

此时基本可以想到是插入过程中出了问题,如果有分发库上的权限完全可以查出具体的错误信息。现在假设数据已经传到订阅数据库,由于某种原因,导致数据没能插入成功。

2、模拟触发器

模拟生产环境下通过触发器将用户表中的数据插入到用户账户信息表。

 create table test1 (id int ,name varchar(20),date datetime default getdate())
create table test2 (id int ,name varchar(20),date datetime default getdate())
create unique nonclustered index [uniqueindex] on [dbo].[test2] ([id] asc)
create trigger tr_test1
on test1
for insert
as
begin
insert into test2(id,name,date)
select * from inserted
end
--连续插入两次
insert into test1 select 1,'abs',getdate()

test2有唯一索引,当往test1中插入相同记录时,会失败(test1、test2插入失败)。

如何将失败的记录获取出来,尝试在触发器用try-catch,并把失败的记录插入到另一张表test3中

 create table test3 (id int ,name varchar(20),date datetime default getdate())
--修改触发器
alter trigger tr_test1 on test1
for insert
as
begin
begin try
insert into test2(id,name,date )
select * from inserted
end try
begin catch
insert into test3(id,name,date )
select * from inserted
end catch
end
--插入测试数据
insert into test1 select 1,'abs',getdate()

当插入相同记录时,同样报错,只是错误信息有所不同

群里请教别人,很快有群友回复

alter trigger tr_test1 on test1
for insert
as
begin
set xact_abort off
begin try
insert into test2(id,name,date )
select * from inserted
end try
begin catch
insert into test3(id,name,date )
select * from inserted
end catch
end
--插入测试数据
insert into test1 select 1,'ere',getdate()

重点是SET XACT_ABORT OFF,此时往Test1插入id已存在于Test2中的记录,就不会报之前的错误。查询三张表的记录如下:

至此已经明白订阅表为什么与发布表不一致。主库每天会按照一定规则从用户信息表删除僵尸用户,订阅表同步删除,但是用户账户表前期并没有删除对应记录。之后ID重用,往订阅端用户信息表写入数据的同时,由于触发器需写入到用户账户表,但是用户账户表在ID字段有唯一约束,insert失败!就出现发布与订阅不一致的情况。
解决方法:确保从用户信息表删除僵尸用户的同时,也从用户账户表删除记录。还可以适当修改触发器让已存在用户账户表的新记录写入到异常用户日志表,方便核对!

3、延伸

将表Test1重命名,然后再创建同结构的Test1,之后在Test1上创建一个Insert触发器。通过sp_helptext triggername查看,此时有两个触发器在Test1上。可实际用的应该是哪个?

 --重命名test1表
sp_rename 'test1','test1_alias','object'
--重新创建test1数据表
create table test1 (id int ,name varchar(20),date datetime default getdate())
--重新创建test1上的触发器,为区分跟踪在里面增加一行注释
create trigger tr_test1_alias on test1
for insert
as
begin
set xact_abort off
begin try
insert into test2(id,name,date )--区分跟踪
select * from inserted
end try
begin catch
insert into test3(id,name,date )--区分跟踪
select * from inserted
end catch
end

此时用sp_helptext查看触发器的定义,发现两个都是在test1上

在sysobjects中看触发器的parent_object

 --先开启跟踪,然后插入记录
insert into test1 select 2,'tst',getdate()

跟踪信息如下,图片中可以看出插入数据使用的是后面创建的触发器tr_test1_alias

此时查询三张表中的记录信息如下所示:

可以在test1_alais上插入数据,看其上的触发器能否使用

 --先开启跟踪,然后插入记录
insert into test1_alias select 3,'uyu',getdate()


从跟踪信息和数据结果可以看出插入数据使用的是触发器tr_test1,虽然sp_helptext tr_test1返回是在test1上,但如果我们在对象资源管理器中,右击对应触发器然后修改或编辑触发器脚本到新窗口,可以看到其指向的是test1_alias。重命名表,与其相关的触发器的parent_object_id指向新表,触发器以对象资源管理器下看到的为准。

捕获Insert触发器失败记录的更多相关文章

  1. [转]连续创建多个Oracle触发器失败,单个创建才成功的解决方法

    连续创建多个Oracle触发器失败,单个创建才成功的解决方法   1.当我连续执行创建多个触发器时,总是报编译通过,但存在警告或错误.如下:   create or replace trigger t ...

  2. SQLServer之创建DML AFTER INSERT触发器

    DML AFTER INSERT触发器创建原理 触发器触发时,系统自动在内存中创建deleted表或inserted表,内存中创建的表只读,不允许修改,触发器执行完成后,自动删除. insert触发器 ...

  3. 记一次SQL Server Insert触发器编写过程

    实现功能:新增特定类型的新闻时,自动追加特定的背景图片. 第一版(错误信息:不能在 'inserted' 表和 'deleted' 表中使用 text.ntext 或 image 列),代码如下: - ...

  4. 【转】MySQL 当记录不存在时insert,当记录存在时update

    MySQL当记录不存在时insert,当记录存在时更新:网上基本有三种解决方法 第一种: 示例一:insert多条记录 假设有一个主键为 client_id 的 clients 表,可以使用下面的语句 ...

  5. MySQL 当记录不存在时insert,当记录存在时update(ON DUPLICATE KEY UPDATE, REPLACE语句)

    MySQL 当记录不存在时insert,当记录存在时更新 网上基本有三种解决方法. 第一种:示例一:insert多条记录 假设有一个主键为 client_id 的 clients 表,可以使用下面的语 ...

  6. MySQL 当记录不存在时insert,当记录存在时update

    MySQL当记录不存在时insert,当记录存在时更新:网上基本有三种解决方法 第一种: 示例一:insert多条记录 假设有一个主键为 client_id 的 clients 表,可以使用下面的语句 ...

  7. MySQL 当记录不存在时insert,当记录存在时更新

    网上基本有三种解决方法. 第一种: 示例一:insert多条记录 假设有一个主键为 client_id 的 clients 表,可以使用下面的语句: INSERT INTO clients (clie ...

  8. SpringMVC 捕获参数绑定失败时的异常

    SpringMVC配置数据验证(JSR-303)中提到了用String类型的域来绑定Ajax中的非法类型的参数. 这样做的目的是一旦发生一种情况,后端可以返回一个自定类的返回值,而不是返回Spring ...

  9. 记一次Tomcat运行失败记录

    记一次Tomcat运行失败记录 如图tomcat运行之后会出现这样的情况,在网上百度之后大部分都说的是web.xml或者其他配置文件的问题,但是根据网上修改了之后却还是老样子. 这里有比较好的网址可以 ...

随机推荐

  1. Java集合框架学习总结

    转自:http://www.cnblogs.com/oubo/archive/2012/01/07/2394639.html Oubo的博客 以下介绍经常使用的集合类,这里不介绍集合类的使用方法,只介 ...

  2. 移动端HTML5资源整理

    目录 meta基础知识 H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 忽略将页面中的数字识别为电话号码 忽略Android平台中对邮箱地址的识别 当网站添加到主屏幕快速启动方式,可隐藏地址栏,仅 ...

  3. ios retain 与 copy 的区别

    .retain 与copy区别 retain 的仅仅是引用计数加1,但是并没有创建新的对象.它们的指针是指向相同的内存地址. copy 是创建一个新的对象作为原来对象的副本,新创建出来的引用计数并没有 ...

  4. UVA 10780 - Again Prime? No Time.

    题目链接 思路好想,注意细节.错了很多次. #include <cstdio> #include <cstring> #include <string> #incl ...

  5. 开篇&TexturePacker打出图集给UGUI使用

    开篇: 前段时间,网上流出了一套手游源码,本想着把服务器端搭一下,给自己认识小伙伴们调试着把这套源码学习一下.于是就买一个阿里云服务器,可是花了几天时间,就是run不起来了啊.还好网上已经有人搭出来了 ...

  6. 自定义plain 样式的 tableview,模拟器上不显示分割线,真机上却显示分割线.

    一, 经历 1> 自定义plain 样式的 tableview,模拟器上不显示分割线,真机上却显示cell 下面的分割线. 2> 尝试使用表格的separatorStyle属性,尝试失败. ...

  7. 在Eclipse中在线安装Emmet和图文使用教程

    ZenCoding 升级为 Emmet 之后,基于 Eclipse 的插件安装地址也发生了变化, 下面是在基于 Eclipse 的 IDE 中安装和使用 Emmet 的图文示例. 一.打开 Eclip ...

  8. Spring中@Async用法总结

    引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3. ...

  9. gerrit使用教程

      注:使用时把“user”替换为自己的账号,例如 ueapp: ssh://huang.fei@10.0.64.16:29418/jonet2_0_app_ueapp.git 新的环境下需要先注册g ...

  10. [CareerCup] 17.7 English Phrase Describe Integer 英文单词表示数字

    17.7 Given any integer, print an English phrase that describes the integer (e.g., "One Thousand ...