您的位置:首页 > 数据库

一次SQLSERVER触发器编写感悟

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

遇到问题能够尝试用不同的方法解决,但不能一味的依照自己的思路走,从问题的根源。从设计者的角度考虑解决方案,总会寻找到的!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: