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

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

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

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

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的语录):问题总是能找到解决方式的,仅仅要你摸清楚设计者的思路。所以一定要多想为什么,人家为啥要这么设计 !

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

一次SQLSERVER触发器编写感悟的更多相关文章

  1. SqlServer触发器的理解

    SqlServer触发器是与表事件相关的特殊存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发.比如当对一个表进行操作( insert,delete, update)时就会激活它执行. ...

  2. SQLServer触发器调用JavaWeb接口

    这几天接到一个需求需要吧不同系统的数据库进行同步,需要我做一个中间平台进行连接,瞬间就想到了触发器调用接口然后通过API进行传递再写入另一个数据库. sqlServer触发器调用JavaWeb接口 1 ...

  3. SqlServer触发器的创建与使用

    前言 上期我们介绍了SqlServer的视图和存储过程创建与使用,这期我们介绍一下触发器. 有需要回顾的可以电梯直达看一下: SqlServer视图的创建与使用 SqlServer存储过程的创建与使用 ...

  4. SQLServer触发器的使用

    创建: create trigger trigger_name on {table_name view_name} {for After Instead of } [ insert, update,d ...

  5. SQLServer存储过程编写规则

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

  6. SqlServer——触发器

    一:触发器基本知识  1.首先必须明确以下几点: 触发器是一种特殊的存储过程,但没有接口(输入输出参数),在用户执行Inserted.Update.Deleted 等操作时被自动触发: 当触发的SQL ...

  7. SQLServer 触发器入门

    阅读目录 一:触发器的优点 二:触发器的作用 三:触发器的分类 四:触发器的工作原理 五:创建触发器 六:管理触发器 概念:   触发器(trigger)是SQL server 提供给程序员和数据分析 ...

  8. sqlserver 触发器实例代码

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

  9. SqlServer触发器的基础知识

    触发器的基础知识:create trigger tr_name on table/view{for | after | instead of } [update][,][insert][,][dele ...

随机推荐

  1. Redis学习笔记(二):Redis集群

    集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能.   1.节点 一个节点就是一个运行在集群模式下的Redis服务器.启动Redis服务器时,通过判断cluster-enabl ...

  2. html body中的标签2

    一.列表标签 列表标签分为三种. 1.无序列表<ul>,无序列表中的每一项是<li> 英文单词解释如下: ul:unordered list,“无序列表”的意思. li:lis ...

  3. A - Supermarket

    Problem description We often go to supermarkets to buy some fruits or vegetables, and on the tag the ...

  4. invoke与call

    “调用一个委托实例” 中的 “调用” 对应的是invoke,理解为 “唤出” 更恰当.它和后面的 “在一个对象上调用方法” 中的 “调用” 稍有不同,后则对应的是call.在英语的语境中,invoke ...

  5. 重装系统后快速安装.NET 3.5

    每一次重装系统(Windows 8.1 和Windows 10)之后,最让我头疼的一件事就是配置把一大堆软件装上了.通常我会装好SQL Server之后,把电脑放在工作组安装Visual Studio ...

  6. 页面中word文本框的编辑,两种方式

    大致效果图(对其中的功能可以增减): 实现方法1:调用js <link href="../../platform/js/kindeditor/themes/default/defaul ...

  7. Redis学习笔记(五)- 数据类型之set类型

    Redis 的set是string类型的无序集合.set元素最大可以包含(2的32次方-1)个元素.set的是通过hash table实现的,所以添加,删除,查找的复杂度都是O(1).hash tab ...

  8. 12.0&12.1 Xcode开发包

    12.1开发包下载链接 12.0开发包下载链接 12.1(16B91)开发包下载链接 Finder打开后,按command+shift+G前往这个地址: /Applications/Xcode.app ...

  9. jQuery学习笔记之jQuery的Ajax(3)

    jQuery学习笔记之jQuery的Ajax(3) 6.jQuery的Ajax插件 源码地址: https://github.com/iyun/jQueryDemo.git ------------- ...

  10. iconfont在ios(safari)中的坑

    最近公司决定将项目图标整体迁移到iconfont,按网上常规方法,在安卓.pc端都没问题,唯独在ios的safari浏览器及微信内置浏览器中,iconfont始终在正常位置向下偏移,导致图标错乱. 网 ...