您的位置:首页 > 其它

在发布订阅中增加rowversion数据类型字段引发错误(or bug)

2011-12-01 13:19 471 查看
考虑这样的场景,现在每天要同步更新的数据,为了记录哪些数据发生过变更,需要对表添加一个rowversion列来标识被更新过。恰好这个表比较大,而且有发布订阅。由于该字段对于订阅表来讲,无实际意义,而且添加一个有默认值的不可为空的8字节字段,会产生数据空间分配,或许带来大量的页拆分,对IO压力会加大,所以考虑将此表的发布项暂时改为不支持架构复制,等添加完该字段后,再将其改为支持架构复制。

下面来做这个测试:

1.下面是一个正常的事务发布,将DB_1中的T_1发布到DB_2的T_1表中。



2.修改发布项DBrepTEST关于架构复制的选项,设置为不支持

DECLARE @publication AS sysname
SET @publication = N'DBrepTEST'

USE DB_1
EXEC sp_changepublication
@publication = @publication,
@property = N'replicate_ddl',
@value = 0
GO


3.然后,添加一个字段,类型为rowversion

use DB_1
alter table t_1 add rv rowversion


4.经过观察,发布订阅一切正常,该字段并未被创建在订阅库DB_2上



5.我们在另外一个进程开始一个sql,向表DB_1.dbo.T_1 insert data.

while 1=1
begin
waitfor delay '00:00:01'
insert into DB_1.dbo.T_1(name)select 'a'
end


6.发现数据已同步到DB_2.dbo.T_1,但rv字段并未被同步,现在一切正常



7.现在将发布项DBrepTEST修改回支持架构复制

DECLARE @publication AS sysname
SET @publication = N'DBrepTEST'

USE DB_1
EXEC sp_changepublication
@publication = @publication,
@property = N'replicate_ddl',
@value = 1
GO


8.然后,此时向表DB_1.dbo.T_1新加一个int类型字段

use DB_1
go
alter table t_1 add age int


9.此时发布订阅出错







出错文字为:
Command attempted:
if @@trancount > 0 rollback tran
(Transaction sequence number: 0x000000580000013C000100000000, Command ID: 6)

Error messages:
A DDL change has been replicated. (Source: MSSQL_REPL, Error number: MSSQL_REPL27332)
Get help: http://help/MSSQL_REPL27332 列名 'rv' 无效。 (Source: MSSQLServer, Error number: 207)
Get help: http://help/207 10.由上面的信息,可以看出此时是由于在订阅库上找不到rv这个列造成的,为了确定这一点,我们看下这个事务执行的是什么

use distribution
go
EXEC sp_browsereplcmds '0x000000580000013C000100000000','0x000000580000013C000100000000'


11.由10的结果中的command列,得到如下需要应用到订阅库的t-sql

ALTER TABLE [dbo].[T_1] add age int
if object_id(N'[dbo].[sp_MSins_dboT_1]', 'P') > 0      drop proc [dbo].[sp_MSins_dboT_1]
--
if object_id(N'dbo.MSreplication_objects') is not null      delete from dbo.MSreplication_objects where object_name = N'sp_MSins_dboT_1'
--
create procedure [dbo].[sp_MSins_dboT_1]
@c1 int,
@c2 varchar(20),
@c3 int
as
begin
insert into [dbo].[T_1](
[id],
[name],
[rv],
[age]
) values (
@c1,
@c2,
default,
@c3 )
end
--
if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null  exec sp_executesql       @statement =           N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type)               values           (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')',      @parameters =           N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname',      @object_n
ame = N'sp_MSins_dboT_1',      @publisher = N'mySrv\SQL2008',      @publisher_db = N'DB_1',      @publication = N'DBrepTEST',      @article = N'T_1'
--
--
if object_id(N'[dbo].[sp_MSdel_dboT_1]', 'P') > 0      drop proc [dbo].[sp_MSdel_dboT_1]
--
if object_id(N'dbo.MSreplication_objects') is not null      delete from dbo.MSreplication_objects where object_name = N'sp_MSdel_dboT_1'
--
create procedure [dbo].[sp_MSdel_dboT_1]
@pkc1 int  as  begin
delete [dbo].[T_1]  where [id] = @pkc1
if @@rowcount = 0      if @@microsoftversion>0x07320000          exec sp_MSreplraiserror 20598
end
--
if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null  exec sp_executesql       @statement =           N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type)               values           (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')',      @parameters =           N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname',      @object_n
ame = N'sp_MSdel_dboT_1',      @publisher = N'mySrv\SQL2008',      @publisher_db = N'DB_1',      @publication = N'DBrepTEST',      @article = N'T_1'
--
--
if object_id(N'[dbo].[sp_MSupd_dboT_1]', 'P') > 0      drop proc [dbo].[sp_MSupd_dboT_1]
--
if object_id(N'dbo.MSreplication_objects') is not null      delete from dbo.MSreplication_objects where object_name = N'sp_MSupd_dboT_1'
--
create procedure [dbo].[sp_MSupd_dboT_1]
@c1 int = NULL,
@c2 varchar(20) = NULL,
@c3 int = NULL,
@pkc1 int = NULL,
@bitmap binary(1)  as  begin
update [dbo].[T_1] set
[name] = case substring(@bitmap,1,1) & 2 when 2 then @c2 else [name] end,
[age] = case substring(@bitmap,1,1) & 4 when 4 then @c3 else [age] end  where [id] = @pkc1
if @@rowcount = 0      if @@microsoftversion>0x07320000          exec sp_MSreplraiserror 20598
end
--
if columnproperty(object_id(N'dbo.MSreplication_objects'), N'article', 'AllowsNull') is not null  exec sp_executesql       @statement =           N'insert dbo.MSreplication_objects (object_name, publisher, publisher_db, publication, article, object_type)               values           (@object_name, @publisher, @publisher_db, @publication, @article, ''P'')',      @parameters =           N'@object_name sysname, @publisher sysname, @publisher_db sysname, @publication sysname, @article sysname',      @object_n
ame = N'sp_MSupd_dboT_1',      @publisher = N'mySrv\SQL2008',      @publisher_db = N'DB_1',      @publication = N'DBrepTEST',      @article = N'T_1'
--
--


可以看到对于添加字段,会重新创建对表操作的三个proc,本例中,insert这个proc会将rv加入进来,并且值为default,如下所示,但由于该列并不存在于订阅中,所以就会报错了,由此造成分发中断,后续事务无法应用到订阅端。

create procedure [dbo].[sp_MSins_dboT_1]
@c1 int,
@c2 varchar(20),
@c3 int
as
begin
insert into [dbo].[T_1](
[id],
[name],
[rv],
[age]
) values (
@c1,
@c2,
default,
@c3 )


12.虽然可以通过在订阅端手工端建一个rv列,将其设置为可为null的varbinary(8)类型,但这个列对于订阅并没有实际意义。

use DB_2
go
alter table t_1 add rv varbinary(8)


总结,经过测试,发现当添加其它类型字段,并设置为有默认值且not null时,不会有此错误出现,目前发现仅是rowversion类型列,不知是否为SQL Server bug.
测试环境:windows server 2003 enterprise 32bit sql server 2008 sp2 standard 32bit
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐