背景: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) beginset 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 insertedend --否则更新server表数据elsebeginupdate shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_idend --COMMIT TRANend try --假设出错则向採集端记录表中插入数据begin catchprint '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_idend catch -- if @boolean='1' -- begin -- print 'boolean'+@boolean -- insert into dbo.test_bak values (@unid,@sensor_id,'insert') -- endend else --假设採集端原表存在该记录则更新该记录beginset 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_idend try --假设出错,推断假设记录表中存在对该条数据的记录。则更新记录表中的记录 --(针对记录表中同一时候存在对同一条数据的create,update,仅仅须要记录终于unid, --假设有create终于仍然向server表create,假设是多次更新仅仅需记录最后一次更新)begin catchif exists(select * from dbo.test_bak where sensor_id=@sensor_id)beginupdate dbo.test_bak set unid=i.unid from inserted iend --假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录elsebegininsert into dbo.test_bak values (@unid,@sensor_id,'update')endend catchend 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) beginset NOCOUNT ON; begin try --推断server表中是否存在该记录 --假设不存在向server表插入该条数据if not EXISTS(SELECT * FROM shengyang.bwdb.dbo.test where sensor_id=@sensor_id)begininsert into shengyang.bwdb.dbo.test select * from insertedend --否则更新server表数据elsebeginupdate shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_idendend trybegin catchprint 'failed to insert this data to server'rollbackend catchendelse --假设採集端原表存在该记录则更新该记录beginset NOCOUNT ON; begin try --更新server表记录update shengyang.bwdb.dbo.test set unid = inserted.unid from inserted where test.sensor_id = @sensor_idend trybegin catchprint 'failed to update this date to server'rollbackend catchend elseif exists(select * from dbo.test_bak where sensor_id=@sensor_id)beginbegin tranupdate dbo.test_bak set unid=i.unid from inserted icommit tranend --假设记录表中不存在对该条数据的改动记录,则在记录标中插入该数据的update记录elsebeginbegin traninsert into dbo.test_bak values (@unid,@sensor_id,'....')commit tranend end
触发器:(非常easy。測试就是通过一个远程查询语句推断)
CREATE PROCEDURE boolean_if_connected AS BEGIN declare @flag varchar(1) begin tryset @flag='0'select * from shengyang.bwdb.dbo.test; end try begin catchset @flag='1'print @flag end catch return @flag end
这样的方法作为推断是可行的,可是。。。。。。
在触发器中调用的时候,假设远程server数据库连接不上了(測试关闭数据库服务),触发的时候直接就报错了,
其它的代码根本就没有运行。
终于。。
。
找到了推断远程链接的方法(此时的心情是激动的。
)
sp_testlinkedserver (Transact-SQL)
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的语录):问题总是能找到解决方式的,仅仅要你摸清楚设计者的思路。所以一定要多想为什么,人家为啥要这么设计 !