How to Alter a Replicated Article in SQL Server 2005
One of the fairly frequently asked questions on the Microsoft replication discussion boards concerns how to alter a replicated article. For simple cases this is quite straightforward - adding a column is achieved using sp_repladdcolumn, while sp_repldropcolumn is used to drop a column. However, what if we want to change an existing column - e.g. change the Fname varchar(50) column below to Fname varchar(100)?
Another article I've written illustrates how this can be achieved in SQL Server 2000; the article lists 2 alternative methods, both of which are rather 'clunky' and result in a huge amount of transactions being replicated. While the article illustrates a useable workaround for DDL changes to replicated tables, there is no correspondingly obvious way of reactively replicating a change to a replicated view or other programming object. So, if we publish a view, when the view definition is changed on the publisher, the new definition may be subsequently pumped to the subscribers using sp_addscriptexec, reinitializing or manually connecting to the subscriber and issuing an 'alter view' statement. However, the problem is that there is no way in SQL 2000 to automatically pick up the view definition changes, and the onus lies on the constant diligence of the DBA to prevent any non-synchronization between the view definition on the publisher and its subscribers. The new DDL replication features now available in SQL Server 2005 may be used to overcome these and other issues and this current article explains how to take advantage of this functionality.
In SQL Server 2005 replication, DDL changes may be replicated for tables, views, stored procedures, functions and triggers. To take advantage of this new methodology is straightforward. For example, adding a column is now done using 'ALTER TABLE' syntax eg
ALTER TABLE tPersonnel ADD Salary INT NULL
Note that the column above is nullable and that this is a requirement - either the new column is nullable or has a default constraint.
During synchronization of the distribution/merge agent, this change is replicated to the subscriber as the same 'ALTER TABLE' statement, along with the requisite stored procedure changes (transactional) and metadata/trigger changes (merge). These DDL changes must always be made at the publisher and never at the subscriber, and this also applies to republishing scenarios, where the schema change will be automatically propagated from the publisher to the publisher-subscriber and finally to the subscriber.
Note that the SQL Management Studio graphical interface may be used to directly add or drop a column to the replicated table, but unfortunately, altering a column is disallowed. On changing an existing column, the GUI chooses to drop and recreate the table behind the scenes, resulting in the error message below:
(In case you're wondering, the 'save text file' button refers to the error message text rather than the necessary TSQL script to run). This is followed by a standard 'User cancelled out of save dialog' message. So, to make the DDL change mentioned initially, we open a query window and run
ALTER TABLE tPersonnel ALTER COLUMN FName VARCHAR(100) NULL
and the changes are replicated as painlessly as when we add a column.
Which DDL changes may now be replicated? Basically almost all aspects of the 'ALTER TABLE' syntax are supported. Adding / dropping / changing a column comes in to this category but remember that index creation uses 'CREATE INDEX' so isn't included and we'll still need to use sp_addscriptexec for this. The exceptions are:
(a) as you would expect for transactional replication, altering a primary key is disallowed and results in an error ('Msg 4929, Level 16, State 1, Line 1 Cannot alter the table 'tPersonnel' because it is being published for replication. Msg 3727, Level 16, State 0, Line 1 Could not drop constraint. See previous errors.')
(b) although it doesn't result in an error, adding an identity column to a published table is not supported, because it can result in non-convergence when the column is replicated to the Subscriber. Altering or dropping identity columns that are managed by replication is also not supported.
The SQL Server 2000 procedure sp_repladdcolumn is still available but is deprecated and is only recommended for exceptional circumstances, namely SQL Server 2000 publishers, SQL Server 2000 republishing subscribers and SQL Server 2000 merge subscribers.
Enabling / Disabling
To control DDL replication, there is a publication property on the 'Subscription Options' section (see below). It can be enabled or disabled on the fly without causing a required reinitialization, and by default is enabled. Once disabled, schema changes are still possible and are not replicated. To achieve the same end in TSQL, use sp_addpublication / sp_addmergepublication / sp_changepublication / sp_changemergepublication specifying a value of 0 for the parameter @replicate_ddl.
The one exception to consider is that 'ALTER TABLE DROP COLUMN' is always replicated to all subscriptions which replicate the column, regardless of the value of the @replicate_ddl parameter.
Working with Different Versions of SQL Server
How does all this tie in if you have multiple versions of SQL Server? The most common combinations will involve a SQL Server 2005 Pub\Dist and a SQL Server 2000 Subscriber and vice versa. The table below shows the resulting behaviour if we add a column:
Finally - Error-Handling
According to BOL setting @replicate_ddl to 0 is the recommended method of making certain changes to avoid errors and enforced reinitialization, with the example being the addition of a foreign key to a publication table when the related table doesn't exist on the subscriber. As far as I can tell this BOL example is obsolete as far as Transactional replication is concerned, as the code used to create the FK constraint (stored in the MSreplCommands table) is prefixed with an 'IF EXISTS' clause which looks for the related table, thus preventing this problem from occurring. The -SkipErrors method would be the first choice to try if DDL replication causes an error, but so far I haven't found a way of testing this as I can't initiate the error itself :). However, in the case of merge replication the schema change command stored in sysmergeschemachange has no such 'IF EXISTS' check so this can indeed result in the error mentioned in BOL. In this case there is a 'get out of jail' card you can play if you are ever in this situation - the stored procedure sp_markpendingschemachange (in conjunction with sp_enumeratependingschemachanges) will mark schema changes to be skipped so synchronization can continue.
Paul Ibison, Copyright © 2013