一次SQLSERVER触发器编写感悟

背景:BOSS须要我写一个工厂採集端到服务器端的数据同步触发器,数据库採用的是sqlserver2008

需求:将多台採集机的数据同步到server中,假设採集端数据库与server数据库连接失败则将数据保存到记录表中

前期思路:从採集端创建server端的数据库链接,通过採集端的insert,update触发。同一时候往远程表写入

问题:因为初始接触sqlserver。对sqlserver触发器了解不深。查阅一些资料后写出了满足正常情况下(连接服务器数据库正常)的触发器。

create trigger trig_sensor_shengyang

on dbo.sensor_test for insert,update as

begin

--假设原表没有该记录则插入该记录

IF NOT EXISTS(SELECT * FROM deleted)

begin

set NOCOUNT ON;

begin tran

--insertopenrowset('sqloledb','XXX.XXX.XXX.XXX';'DBUSER';'DBPWD',bwdb.dbo.test)

--向server表插入该条数据

insert into shengyang.bwdb.dbo.test select * from inserted

--同一时候向记录表中插入数据

insert into dbo.test_bak values((select unid from inserted),(select sensor_id from inserted),'create')

commit tran

end

else

--假设原表存在该记录则更新该记录

begin

set NOCOUNT ON;

begin tran

--update openrowset('sqloledb','XXX.XXX.XXX.XXX';'DBUSER';'DBPWD',bwdb.dbo.test)

--更新server表记录

update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted

--推断假设记录表中存在对该条数据的记录,则更新记录表中的记录

--(针对记录表中同一时候存在对同一条数据的create,update。仅仅须要记录终于unid。

--假设有create终于仍然向server表create,假设是多次更新仅仅需记录最后一次更新)

if exists(select * from dbo.test_bak where sensor_id=(select sensor_id from inserted))

begin

update dbo.test_bak set unid=i.unid from inserted i

end

--假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录

else

begin

insert into dbo.test_bak values ((select unid from inserted),(select sensor_id from inserted),'update')

end

commit tran

end

end

可是因为须要考虑两方网络不通的情况,因此须要做异常处理。開始没查找到推断远程数据库连接的方法,因此想着直接通过try catch来实现(try块里面运行可能出现异常的——往远程server端写入的代码,catch块里写往採集端本地记录表中的代码)

create trigger trig_sensor_shengyang 
on dbo.sensor_test after insert,update as
declare @unid varchar(20)
declare @sensor_id varchar(8)
declare @boolean varchar(1)
begin
set @unid = (select unid from inserted)
set @sensor_id = (select sensor_id from inserted)

--假设採集端原表没有该记录则插入该记录
IF NOT EXISTS(SELECT * FROM deleted)
begin
set NOCOUNT ON;
begin try
-- BEGIN TRAN
--推断server表中是否存在该记录
--假设不存在向server表插入该条数据
print '1111111111'
if not EXISTS(SELECT * FROM shengyang.bwdb.dbo.test where sensor_id=@sensor_id)
begin
--insert openrowset('sqloledb','XXX.XXX.XXX.XXX';'DBUSER';'DBPWD',bwdb.dbo.test)
insert into shengyang.bwdb.dbo.test select * from inserted
end
--否则更新server表数据
else
begin
update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id
end
--COMMIT TRAN
end try

--假设出错则向採集端记录表中插入数据
begin catch
print 'fail to insert this data to server'
rollback
-- print @@TRANCOUNT
-- IF @@TRANCOUNT > 0---------------推断有没有事务
-- BEGIN
-- ROLLBACK TRANSACTION ts----------回滚事务
-- END 
insert into dbo.test_bak values (@unid,@sensor_id,'insert')
set @boolean = '1'

--EXEC insert_sensor_shengyang @unid,@sensor_id
end catch
-- if @boolean='1'
-- begin
-- print 'boolean'+@boolean
-- insert into dbo.test_bak values (@unid,@sensor_id,'insert')
-- end
end
else
--假设採集端原表存在该记录则更新该记录
begin
set NOCOUNT ON; 
begin try
--update openrowset('sqloledb','XXX.XXX.XXX.XXX';'DBUSER';'DBPWD',bwdb.dbo.test)
--更新server表记录
update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id
end try
--假设出错,推断假设记录表中存在对该条数据的记录。则更新记录表中的记录
--(针对记录表中同一时候存在对同一条数据的create,update。仅仅须要记录终于unid,
--假设有create终于仍然向server表create,假设是多次更新仅仅需记录最后一次更新)
begin catch
if exists(select * from dbo.test_bak where sensor_id=@sensor_id)
begin
update dbo.test_bak set unid=i.unid from inserted i
end
--假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录
else
begin
insert into dbo.test_bak values (@unid,@sensor_id,'update')
end
end catch
end
end

可是不管如何,仅仅要出现异常,就会强制回滚。此时假设在catch块之前提交,触发的仍然时候就会报错,而且无法将错误的记录插入异常记录表(运行不到),触发的原表记录能够写入。假设在catch块中rollback,然后将该记录插入异常记录表能够,可是同一时候回滚后触发的原记录也回滚丢失了。假设在catch块中commit,也不行(catch块中默认回滚了全部事务),包含尝试了使用记录回滚点进行分段事务提交回滚还是无法解决。

既不能commit,又不能rollback,这如何是好。。。

。。

随后BOSS提了个建议。通过存储过程中先做异常处理。推断server数据库是否连接成功。随即写了个存储过程,在存储过程中訪问远程数据库。定义一个变量初始值,catch块中改动这个值,然后把这个值作为存储过程返回值进行推断。

触发器:

create trigger trig_sensor_shengyang 
on dbo.sensor_test after insert,update as
declare @unid varchar(20)
declare @sensor_id varchar(8)
declare @boolean varchar(1)
declare @ifconnected varchar(2)
begin
set @unid = (select unid from inserted)
set @sensor_id = (select sensor_id from inserted)

--调用存储过程推断远程连接server以及同步事务开启是否成功。返回1则表示失败
--sp_testlinkedserver [ @servername ] = servername
EXEC @ifconnected = [boolean_if_connected] 
print @ifconnected
--假设远程连接成功
IF @ifconnected != 1
--假设採集端原表没有该记录则插入该记录
IF NOT EXISTS(SELECT * FROM deleted)
begin
set NOCOUNT ON;
begin try
--推断server表中是否存在该记录
--假设不存在向server表插入该条数据
if not EXISTS(SELECT * FROM shengyang.bwdb.dbo.test where sensor_id=@sensor_id)
begin
insert into shengyang.bwdb.dbo.test select * from inserted
end
--否则更新server表数据
else
begin
update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id
end
end try
begin catch
print 'failed to insert this data to server'
rollback
end catch
end
else
--假设採集端原表存在该记录则更新该记录
begin
set NOCOUNT ON; 
begin try
--更新server表记录
update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id
end try
begin catch
print 'failed to update this date to server'
rollback
end catch
end
else
if exists(select * from dbo.test_bak where sensor_id=@sensor_id)
begin
begin tran
update dbo.test_bak set unid=i.unid from inserted i
commit tran
end
--假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录
else
begin
begin tran
insert into dbo.test_bak values (@unid,@sensor_id,'....')
commit tran
end
end

触发器:(非常easy,測试就是通过一个远程查询语句推断)

CREATE PROCEDURE boolean_if_connected
AS
BEGIN 
declare @flag varchar(1)
begin try
set @flag='0'
select * from shengyang.bwdb.dbo.test;
end try
begin catch
set @flag='1'
print @flag
end catch
  return @flag
end

这样的方法作为推断是可行的。可是。。。。

。。在触发器中调用的时候,假设远程server数据库连接不上了(測试关闭数据库服务),触发的时候直接就报错了,

其它的代码根本就没有运行。

终于。

找到了推断远程链接的方法(此时的心情是激动的。)

sp_testlinkedserver (Transact-SQL)

https://msdn.microsoft.com/zh-cn/library/ms189809(v=sql.90).aspx
通过该方法可直接推断创建的远程server连接是否有效。

终于触发器測试代码例如以下:

create trigger trig_sensor_shengyang

on dbo.sensor_test after insert,update as

declare @unid varchar(20)

declare @sensor_id varchar(8)

declare @boolean varchar(1)

declare @ifconnected varchar(2)

begin

set @unid =(select unid from inserted)

set @sensor_id =(select sensor_id from inserted)

--调用存储过程推断远程连接server以及同步事务开启是否成功,返回则表示失败

--sp_testlinkedserver[ @servername ] = servername

EXEC @ifconnected = [sp_testlinkedserver]shengyang

print @ifconnected

--假设远程连接成功

IF @ifconnected != 1

--假设採集端原表没有该记录则插入该记录

IF NOTEXISTS(SELECT * FROM deleted)

begin

set NOCOUNT ON;

begin try

--推断server表中是否存在该记录

--假设不存在向server表插入该条数据

if not EXISTS(SELECT * FROM shengyang.bwdb.dbo.test where sensor_id=@sensor_id)

begin

insert into shengyang.bwdb.dbo.test select * from inserted

end

--否则更新server表数据

else

begin

update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id

end

end try

begin catch

print 'failed to insert to server'

rollback

end catch

end

else

--假设採集端原表存在该记录则更新该记录

begin

set NOCOUNT ON;

begin try

--更新server表记录

update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_id

end try

begin catch

print 'failed to update to server'

rollback

end catch

end

else

if exists(select * from dbo.test_bak where sensor_id=@sensor_id)

begin

begin tran

update dbo.test_bak set unid=i.unid from inserted i

commit tran

end

--假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录

else

begin

begin tran

insert into dbo.test_bak values (@unid,@sensor_id,'....')

commit tran

end

end

总结(用BOSS的语录):问题总是能找到解决方式的,仅仅要你摸清楚设计者的思路,所以一定要多想为什么,人家为啥要这么设计 !

遇到问题能够尝试用不同的方法解决,但不能一味的依照自己的思路走,从问题的根源。从设计者的角度考虑解决方案,总会寻找到的!

posted on 2017-06-20 09:02 mthoutai 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/mthoutai/p/7052606.html

一次SQLSERVER触发器编写感悟相关推荐

  1. sqlserver 触发器实例代码

    何为触发器?在SQL Server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序.触发器是一个特殊的存储过程. 常见的触发器有三种:分别应用于Insert , Update , D ...

  2. sqlserver 触发器 怎么获取更新前的值

     sqlserver 触发器 怎么获取更新前的值 更新的动作你可以分2步理解,先delete ,再insert 所以,前的值在 deleted里   后的值在 inserted里 create t ...

  3. Sqlserver 触发器

    Sqlserver 触发器 触发器是在对数据表 新增 修改 和删除的时候 进行一次自定义事件的处理 就是 比如 为新增一条数据 的时候 自动触发一个事件 来验证这个数据在别的表的关系  不然还得在应用 ...

  4. SqlServer触发器计算年休假天数

    SqlServer触发器计算年休假天数 表结构 触发器代码 根据插入语句中的进入本单位工作时间字段,自动计算年休假天数 数据库:sqlsever2012 表结构 触发器代码 SET ANSI_NULL ...

  5. sqlserver触发器的使用以及inserted和deleted详解

    背景:最近在项目中有需求是当人员表中有变动时(比如:增加人员.修改人员信息.删除人员信息)需要把这张表中的变动的信息同步到它对应的日志表中.那么如果用代码写逻辑的话在执行效率上会比较慢,正好sqlse ...

  6. SqlServer触发器使用整理

    一.触发器的使用场景.优缺点 1.强制业务规则和数据完整性. SqlServer中提供了2中方案,约束(主键约束.外键约束.Not Null约束.唯一约束以及检查约束)和触发器 触发器,可以编写T-s ...

  7. sqlserver 触发器 update_运维日记| SQL server 那点事——DML触发器

    各位新朋友-记得先点蓝字关注我哦- 11月19日,21点,小编正六指霸屏,决赛圈1V4,忽然,电话响了,这种感觉很熟悉,不错,上次差点推掉对面水晶的那一幕又上演了--作为一名美创的员工,客户才是第一位 ...

  8. SQLServer存储过程编写规则

    SQLServer编写规则 1.  存储过程 a)         在程序应用中,对于数据库"写"操作的功能通过存储过程来实现. b)        存储过程命名: SP_+表名( ...

  9. SQLServer触发器的使用

    基本语法 创建: create trigger trigger_name on {table_name view_name} {for After Instead of } [ insert, upd ...

最新文章

  1. Can't create handler inside thread Thread that has not called Looper.prepare()
  2. 【转】头文件和库文件区别
  3. Oracle中NVARCHAR2与VARCHAR2的区别
  4. 六、数据的基本统计描述
  5. Android MVP模式的初识
  6. ASP.NET Core中Ocelot的使用:基于服务发现的负载均衡
  7. svn locked解决方法
  8. 信息论 哈夫曼编码 与 菲诺编码的实现(对一幅BMP格式的灰度图像(个人 证件照片)进行二元霍夫曼编码和译码。并进行编码效率的计算,对一幅BMP格式的灰度图像进行二 元Fano编码、译码 )
  9. ionic3 html调用摄像头,ionic3懒加载中使用自定义组件component
  10. “123456”连续七年霸榜,2019最糟糕密码榜单出炉
  11. 中国通信简史 (上)
  12. 基于vscode的vue开发
  13. 证件照(1寸2寸)拍摄处理知识汇总
  14. 计算机的游戏功能,游戏同步器功能介绍、作用讲解及使用方法
  15. Preparing transaction:done Verifying transaction:failed RemoveError:‘requests‘ is a dependency of **
  16. ping服务器响应39ms,美国服务器的ping值多少网速才算快?
  17. BeyondCompare的三种破解方法
  18. 程序员,你何时离开北京
  19. SpringSecurity前后端分离02(授权)
  20. 生活随记 - 不知名的花儿

热门文章

  1. Java调用 shell脚本阻塞
  2. 有关nginx location规则
  3. JAVA实现环形缓冲多线程读取远程文件
  4. 转:在windows通过Xrdp软件远程桌面连接Fedora
  5. css3动画,监控动画执行完毕
  6. js模拟栈---进制转化。十进制转任意进制进制,任意进制转十进制
  7. 2018上C语言程序设计(高级)作业-第1次作业
  8. 2. Oracle 数据库实例启动关闭过程
  9. Python的数据库mongoDB的入门操作
  10. 常见的水平居中布局方式