none
sqlserver2008 使用事务发布订阅的方式做数据同步,两个数据库的结构完全一致,订阅端设置为不执行触发器,发布端由触发器增、删、改的数据能同步到发布端吗? RRS feed

  • 问题

  • sqlserver2008 使用事务发布订阅的方式做数据同步,两个数据库的结构完全一致,订阅端设置为不执行触发器,发布端由触发器增、删、改的数据能同步到发布端吗?
    2014年4月9日 5:39

答案

  • 能,不过要注意设置

    “Not for Replication”选项

    “Not for Replication”是在SQL Server复制中可应用于各个对象(例如Check约束、外键约束、触发器、标识列-Identity Column等)的属性。
    当数据库管理员想要让事务的行为在数据修改变更来自复制代理与来自一般用户事务的两种情况下不一样时,使用该特性。

    范例说明

    某公司在全国各地有多个销售点,每个销售点从终端用户得到订单并将请求复制到总公司的产品与派发部门。

    总公司产品与派发部门的服务器作为发布方,各销售点作为订阅方,搭建成合并复制架构。每个销售点存有产品当前库存级别的信息;当主库商品售罄后,销售点将不再接受新订单。

    复制拓扑中的表如下:Products为父表,与Orders表以Product_id列通过主键-外键约束进行连接。Product表还包括名为Stock_Available的列,用于跟踪产品的当前库存情况。

    Products:

    Orders:

    除此之外,在Orders表上还有一个AFTER INSERT触发器(名为“Update_Stock”),用来检查对应于某product_id的商品有没有足够的库存。如果有,它会更新Products表,从当前库存中减去数量。

    触发器示例:

    create TRIGGER [dbo].[Update_Stock]

       ON  [dbo].[Orders]

       AFTER  INSERT

    AS

    BEGIN

    declare @quantity as int

    declare @product_id as int

    declare @stock_available as int

    select @product_id= product_id , @quantity = quantity from Inserted

    select @stock_available= stock_available from Products where product_id=@product_id

    if (@Quantity > @stock_available)

        rollback

    else

        update Products set stock_available= stock_available- @quantity whereproduct_id=@product_id

    END

    现在我们假设,在某个订阅服务器(这里称它为订阅方#1”)上,有产品”Baseball Bats”Product_id=88)的一个新的订单。在订阅方#1插入数据后,显示如下:

    Orders:

    这个插入(对应第三行)操作将会引发插入触发器,使得Products表中,对应Product_id=88Stock_Available(现有库存)列上的值减20

    Products:

    当该事务在订阅方#1完成后,订阅方#1与发布方同步。现在复制检测到,在订阅方#1曾经有两个事务需要更新到主发布服务器上:

    1.   Orders表上的插入操作

    2.   Products表上Stock_Available列的更新操作。(复制不管更新是否是来自触发器,对于复制来说,这只是Products表上的一个一般的更新事务)

    在同步完成后,订阅方#1上的数据与主发布方的数据合并,两方的表看起来应该是一样的。但是,看下面在第一次同步完成后,从发布方得到的数据:

    Orders

        

     

    Products:

    发现了吧,问题出现了!发布方的Stock_Available列应该为80而不是60

    这是为什么呢?

    Products是父表,Orders是子表,默认情况下合并复制的同步顺序会先为父表复制事务然后再是子表。正如前面所说的,插入触发器所做的更新也被视为一般用户事务。Products表作为父表,更新事务首先到发布方将Stock_Available列设置为80。接下来Orders表上的插入操作也被应用到发布方,同时触发表上的AFTER INSERT触发器,这个触发器回到Products表并重新更新Stock_Available列的值,将Quantity值变成了6080-20=60)。

    也就是说,如果在第一次同步之后,而没有进一步变化之前,再次进行一次复制同步,发布方和订阅方的Stock_Available列将会有同一个错误值(例子中的60)。

    如何避免这种情况?

    这里就引出了“Not for Replication”属性存在的意义。我们的目的是:如果INSERT来自复制合并代理,它就不需要引发触发器;如果INSERT是来自一般用户操作,触发器应被触发来更新当前库存数据。我们可以通过在所有复制相关的服务器和数据库上设置并启用触发器的“Not for Replication”属性来实现。

    如果不指定就会变成双重触发,发布那边触发,订阅也触发

    具体网址:http://blogs.msdn.com/b/apgcdsd/archive/2012/04/25/not-for-replication.aspx

    • 已标记为答案 yqs1982410 2014年4月10日 1:02
    2014年4月9日 10:57