Altering
Replicated Articles in SQL Server 2005
Introduction
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 - eg 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.
Implementation
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 2005 Pub\Dist and a SQL 2000
Subscriber and vice versa. The table below shows the resulting behaviour if we
add a column:
| Publisher\Distributor |
Subscriber |
Behaviour |
| SQL 2000 |
SQL 2005 |
Alter Table does nothing to the subscriber while
sp_repladdcolumn will add the column (both same as
2000 -> 2000) |
| SQL 2005 |
SQL 2000 |
Alter Table replicates the change
in transactional but not merge (as
mentioned above, we still use sp_repladdcolumn and
sp_repldropcolumn for these cases) |
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.
|