• 一次SQLSERVER触发器编写感悟


    背景: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的语录):问题总是能找到解决方式的,仅仅要你摸清楚设计者的思路。所以一定要多想为什么,人家为啥要这么设计 !
    遇到问题能够尝试用不同的方法解决。但不能一味的依照自己的思路走。从问题的根源,从设计者的角度考虑解决方式,总会寻找到的!


  • 相关阅读:
    Java从指定目录下加载class文件
    超市问题
    SpringBoot中的thymeleaf布局
    SpringBoot 异步支持
    Groovy 脚本引发的 Old GC问题
    Java8 新特性笔记
    MySQL字段默认值踩坑记录
    SpringMVC接收Postman post json数据
    如何实现远程办公安全
    打印机打印的文档或图像出现白色横纹如何解决?
  • 原文地址:https://www.cnblogs.com/llguanli/p/8453225.html
Copyright © 2020-2023  润新知