Writes, optimizes, and debugs T-SQL queries. Explains SQL Server internals, troubleshoots performance issues, and guides database administration tasks including backup/restore, high availability, security, and index design. Use when the user asks about T-SQL syntax, SQL Server administration, query performance, stored procedures, indexes, locking, transactions, backup/restore, high availability, security, or any MSSQL-related topic — even without saying 'SQL Server' explicitly. Also trigger on terms like SSMS, tempdb, bcp, sqlcmd, MSSQL, sp_executesql, NOLOCK, columnstore, Hekaton, RCSI, param sniffing, or execution plan.
100
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Use replication when you need:
Do not use replication when:
| Type | Latency | Conflict | Best for |
|---|---|---|---|
| Snapshot | Minutes to hours | No conflict model | Reference data, initial seeding, small tables refreshed periodically |
| Transactional | Sub-second to seconds | Publisher wins (unidirectional) | High-throughput OLTP distribution, reporting subscriber |
| Merge | Seconds to hours | Configurable resolution | Mobile/occasionally connected clients, bidirectional multi-master |
| Peer-to-Peer (P2P) | Sub-second to seconds | Conflict detection only (no resolution) | Multi-datacenter active-active with conflict avoidance |
Transactional replication is the most common choice for SQL Server shops distributing OLTP data to reporting subscribers. Snapshot is the seeding mechanism for transactional replication and works alone for small, infrequently changed tables. Merge replication has largely been supplanted by modern sync frameworks for mobile scenarios.
Publisher DB Distributor DB Subscriber DB(s)
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Published │ Log │ distribution │ │ Subscribed │
│ tables │─────▶│ database │─────▶│ tables │
│ (articles) │Reader│ (msrepl_trans │ Dist │ │
└──────────────┘ │ + msrepl_cmds)│Agent └──────────────┘
└─────────────────┘distribution database — a work queue of changes. Can co-reside on the publisher (local distributor) or be a separate server (remote distributor). Remote distributor is preferred for high-volume workloads.Snapshot replication copies the complete state of published articles at scheduled intervals. It does not use the transaction log — the Snapshot Agent bulk copies data to snapshot files, then the Distribution Agent applies them.
Workflow:
.sch schema and .bcp data files to the snapshot share.Use cases:
Limitations:
Transactional replication reads the transaction log on the publisher continuously and delivers committed transactions to subscribers, typically with sub-second to seconds of latency.
Data flow:
distribution database.distribution database and applies commands to the subscriber using parameterized stored procedures (the default delivery mechanism — fast and auditable).Key properties:
sp_MSins_, sp_MSupd_, sp_MSdel_)[!WARNING] Log growth risk If the Log Reader Agent falls behind or is stopped, the publisher's transaction log cannot be truncated. Monitor
log_reuse_wait_desc = REPLICATIONinsys.databases.
Immediate updating / queued updating subscriptions: Legacy features, rarely used, effectively deprecated in favor of P2P replication or merge.
Merge replication allows changes at both publisher and subscriber to be synchronized bidirectionally. It uses rowguid columns on every article table to track changes.
Key properties:
rowguid uniqueidentifier ROWGUIDCOL column to every published table (if not present — a schema intrusion)MSmerge_contents, MSmerge_tombstone, and MSmerge_genhistory system tablesHOST_NAME() / SUSER_SNAME() for per-subscriber data partitioningAgents:
[!WARNING] Deprecated trajectory Merge replication is functionally complete but no longer receiving new investment. For bidirectional sync scenarios, evaluate Azure SQL Data Sync or application-layer sync instead.
| Agent | Runs at | Role | Applicable types |
|---|---|---|---|
| Snapshot Agent | Distributor | Generates schema + data snapshot files | Snapshot, Transactional (initial), Merge |
| Log Reader Agent | Distributor | Reads publisher log, writes to distribution DB | Transactional |
| Distribution Agent | Distributor (push) or Subscriber (pull) | Applies changes from distribution DB to subscriber | Snapshot, Transactional |
| Merge Agent | Distributor (push) or Subscriber (pull) | Bidirectional sync | Merge |
| Queue Reader Agent | Distributor | Handles queued updating subscriptions | Transactional (queued updating) |
| Replication Monitor | SSMS/msdb | Monitoring only | All |
All agents run as SQL Agent jobs. Check msdb.dbo.sysjobs filtered by job name pattern %repl% or use Replication Monitor.
Below is a minimal T-SQL setup for a same-server publisher + local distributor + push subscriber. In production, replace [local] with a remote distributor server.
-- 1. Configure the distributor (run on distributor server, here same as publisher)
USE master;
EXEC sp_adddistributor
@distributor = @@SERVERNAME,
@password = 'DistributorPassword!1';
EXEC sp_adddistributiondb
@database = 'distribution',
@data_folder = 'C:\SQLData',
@log_folder = 'C:\SQLLogs',
@log_file_size = 2,
@min_distretention = 0,
@max_distretention = 72, -- hours; increase for high latency subscribers
@history_retention = 48,
@deletebatchsize_xact = 5000,
@deletebatchsize_cmd = 2000;
-- 2. Register the publisher with the distributor
EXEC sp_adddistpublisher
@publisher = @@SERVERNAME,
@distribution_db = 'distribution',
@security_mode = 1, -- Windows auth for agent connections
@working_directory = '\\ServerName\ReplSnap\';
-- 3. Enable the publishing database
USE [YourPublisherDB];
EXEC sp_replicationdboption
@dbname = 'YourPublisherDB',
@optname = 'publish',
@value = 'true';
-- 4. Add a publication
EXEC sp_addpublication
@publication = 'MyPublication',
@description = 'Transactional publication',
@sync_method = 'concurrent', -- uses BCP with open cursor
@retention = 336, -- hours subscription can be inactive
@allow_push = 'true',
@allow_pull = 'true',
@allow_anonymous = 'false',
@enabled_for_internet = 'false',
@snapshot_in_defaultfolder = 'true',
@compress_snapshot = 'false',
@repl_freq = 'continuous',
@status = 'active',
@independent_agent = 'true',
@immediate_sync = 'false',
@allow_sync_tran = 'false',
@autogen_sync_procs = 'true',
@allow_queued_tran = 'false',
@allow_dts = 'false',
@replicate_ddl = 1; -- replicate DDL changes
-- 5. Add a Log Reader Agent job
EXEC sp_addlogreader_agent
@publisher_security_mode = 1; -- Windows auth
-- 6. Add articles (one per table)
EXEC sp_addarticle
@publication = 'MyPublication',
@article = 'Orders',
@source_owner = 'dbo',
@source_object = 'Orders',
@type = 'logbased',
@description = NULL,
@creation_script = NULL,
@pre_creation_cmd = 'drop', -- drop existing table at subscriber, recreate
@schema_option = 0x000000000803509F,
@identityrangemanagementoption = 'manual',
@destination_table = 'Orders',
@destination_owner = 'dbo',
@vertical_partition = 'false';
-- 7. Generate initial snapshot
EXEC sp_startpublication_snapshot
@publication = 'MyPublication';
-- 8. Add a push subscription
EXEC sp_addsubscription
@publication = 'MyPublication',
@subscriber = 'SubscriberServerName',
@destination_db = 'YourSubscriberDB',
@subscription_type = 'Push',
@sync_type = 'automatic', -- apply snapshot on first run
@article = 'all',
@update_mode = 'read only';
-- 9. Add the Distribution Agent job for this push subscription
EXEC sp_addpushsubscription_agent
@publication = 'MyPublication',
@subscriber = 'SubscriberServerName',
@subscriber_db = 'YourSubscriberDB',
@subscriber_security_mode = 1,
@frequency_type = 64, -- continuous
@frequency_interval = 0,
@frequency_subday_type = 4,
@frequency_subday_interval = 5;[!NOTE] SQL Server 2022 The
@publisher_login/@publisher_passwordparameters can be replaced with service account Windows auth (@security_mode = 1) which is recommended. Always use Windows auth or a dedicated SQL login with minimal permissions — avoidsa.
-- Static row filter: only replicate orders for region 'West'
EXEC sp_addarticle
@publication = 'MyPublication',
@article = 'Orders',
@source_object = 'Orders',
@filter_clause = N'Region = ''West''';
-- After adding the filter, add the filter proc
EXEC sp_articlefilter
@publication = 'MyPublication',
@article = 'Orders',
@filter_name = 'FLTR_Orders',
@filter_clause = N'Region = ''West''';
EXEC sp_articleview
@publication = 'MyPublication',
@article = 'Orders';-- Exclude sensitive columns (e.g., CreditCardNumber)
EXEC sp_articlecolumn
@publication = 'MyPublication',
@article = 'Customers',
@column = 'CreditCardNumber',
@operation = 'drop'; -- 'add' to re-includeRules:
sp_replcmds text-based format instead of the faster binary format — avoid if possible| Aspect | Push | Pull |
|---|---|---|
| Distribution Agent runs at | Distributor | Subscriber |
| Scheduling | Always-running (continuous) | Subscriber controls schedule |
| Central management | Easier | Harder |
| Network load | Outbound from distributor | Subscriber initiates |
| Common use | Internal server-to-server | Remote / occasionally connected subscribers |
| DMZ / firewall | Distributor must reach subscriber | Subscriber reaches out (easier through NAT) |
For always-connected subscribers on the same network, push is preferred — continuous mode gives lower latency and simpler monitoring.
| Capability | Transactional Replication | CDC | AG Readable Secondary |
|---|---|---|---|
| Data movement | To separate database/server | Same server, change tables | Same data, different replica |
| Latency | Sub-second to seconds | Near real-time (log-based) | Near real-time (log apply) |
| Subscriber can query live | Yes (separate DB) | Yes (change tables) | Yes (read queries on secondary) |
| Subset of data | Yes (row/column filters) | Table-level granularity | No — full database copy |
| Bidirectional | P2P only | No | No |
| Requires AG license | No | No | Yes (Enterprise for readable secondary) |
| Cross-server | Yes | No (same server) | Yes |
| Schema changes | Managed with replicate_ddl | Requires re-enabling | Automatic |
| Conflict handling | Publisher wins | N/A | N/A |
| Azure SQL DB support | Subscriber only | Yes | N/A (Hyperscale HA replicas) |
Decision rule:
Right-click the replication folder in SSMS or use exec sp_replmonitorhelppublication to open. Shows latency, agent history, subscription expiry.
-- Undistributed commands (backlog in distribution DB)
USE distribution;
SELECT TOP 20
a.publisher_db,
a.publication,
a.subscriber_db,
a.subscriber,
b.undistrib_cmds,
b.dist_db_name
FROM MSdistribution_status b
JOIN MSsubscriptions a ON b.agent_id = a.agent_id
ORDER BY b.undistrib_cmds DESC;
-- Replication latency tracer tokens (insert a token, measure delivery time)
EXEC sp_posttracertoken
@publication = 'MyPublication';
-- Check tracer token delivery time
EXEC sp_helptracertokens
@publication = 'MyPublication';
-- Log reader status
SELECT * FROM MSlogreader_agents;
-- Distribution agent history (last 50 runs)
SELECT TOP 50
a.name,
h.start_time,
h.time,
h.duration,
h.delivered_transactions,
h.delivered_commands,
h.delivery_rate,
h.error_id,
h.comments
FROM MSdistribution_history h
JOIN MSdistribution_agents a ON h.agent_id = a.id
ORDER BY h.time DESC;
-- Check subscriptions status
SELECT
srv.srvname AS subscriber,
sub.dest_db,
pub.publication,
sub.status, -- 0=inactive, 1=subscribed, 2=active
sub.sync_type,
sub.nosync_type
FROM MSsubscriptions sub
JOIN MSarticles art ON sub.artid = art.artid
JOIN MSpublications pub ON art.pubid = pub.pubid
JOIN master.sys.servers srv ON sub.srvid = srv.srvid;-- Check if log is held by replication on publisher
SELECT
name,
log_reuse_wait,
log_reuse_wait_desc,
log_size_mb = log_size_mb,
log_used_mb = log_used_mb,
log_used_pct = CAST(log_used_mb * 100.0 / NULLIF(log_size_mb,0) AS decimal(5,1))
FROM sys.databases
CROSS APPLY (
SELECT
CAST(FILEPROPERTY(name,'SpaceUsed')/128.0 AS decimal(10,1)) AS log_used_mb,
CAST(size/128.0 AS decimal(10,1)) AS log_size_mb
FROM sys.master_files
WHERE database_id = sys.databases.database_id AND type = 1
) f
WHERE log_reuse_wait_desc = 'REPLICATION';| Failure | Symptom | Fix |
|---|---|---|
| Log Reader not running | Publisher log grows; log_reuse_wait_desc = REPLICATION | Start Log Reader Agent job; fix underlying error in agent history |
| Snapshot share inaccessible | Distribution Agent fails on first sync | Verify UNC path, permissions for agent service account |
| Schema mismatch | Distribution Agent error: column count or type mismatch | Check subscriber schema; run sp_refreshsubscriptions or re-initialize |
| Subscription expiry | Error 21074: subscription expired | Re-initialize with snapshot or increase @retention on publication |
| Deadlock on subscriber | Distribution Agent retry loop | Ensure replication procs don't conflict with user workload; increase @pollinginterval |
| Distributor full | distribution database out of space | Increase max size; raise @max_distretention; investigate stalled subscribers |
| Duplicate key at subscriber | Error 2627 on Distribution Agent | Check for out-of-band inserts at subscriber; re-initialize or manually fix the row |
| Log Reader latency spike | Undistributed commands climb | Publisher under heavy DML load; scale up distributor or reduce replication frequency |
P2P replication (Enterprise Edition) allows multiple nodes to be both publishers and subscribers, making all nodes writable.
[!NOTE] SQL Server 2019+ P2P replication added conflict detection (not resolution) — conflicts are detected and the transaction is blocked/rolled back. This means you must architect to avoid conflicts (partition writes by node, use sequences with different seeds, etc.).
-- Enable P2P on a publication
EXEC sp_changepublication
@publication = 'MyPublication',
@property = 'allow_initialize_from_backup',
@value = 'true';
-- Check P2P topology
SELECT * FROM MSpeer_topologyrequest;
SELECT * FROM MSpeer_topologyresponse;P2P requirements:
NEWSEQUENTIALID())@p2p_conflictdetection = 'true' — raises error on conflict, you must have retry logicWhen @replicate_ddl = 1 (the default), most DDL changes to published articles are automatically replicated. However, some DDL operations require manual steps.
-- Add a nullable column (replicated automatically with replicate_ddl=1)
ALTER TABLE dbo.Orders ADD ShippingNotes NVARCHAR(500) NULL;
-- After the DDL replicates, verify the article schema is refreshed
EXEC sp_refresharticleview
@publication = 'MyPublication',
@article = 'Orders';DDL operations that require re-initialization (cannot be auto-replicated):
sp_rename does not replicate)Safe workflow for risky DDL:
When the publisher database is in an AG, the Log Reader Agent must know to read from the primary replica (it follows the AG listener automatically after SQL 2012 SP2+).
-- Redirect the distributor to use the AG listener for the publisher
EXEC sys.sp_redirect_publisher
@original_publisher = 'OriginalPublisherNode1',
@publisher_db = 'YourPublisherDB',
@redirected_publisher = 'AGListenerName'; -- DNS name of the AG listener
-- Verify redirect
SELECT * FROM sys.dm_repl_articles; -- check source
SELECT * FROM distribution.dbo.MSdistribution_agents;Key considerations:
sp_redirect_publisher must point to the new primary (or the listener handles it automatically if already set)sp_MSrepl_check_publisher_connection to verify connectivity after failover@subscriber as the AG listener name| Scenario | Supported |
|---|---|
| Azure SQL Database as publisher | No |
| Azure SQL Managed Instance as publisher | Yes (full transactional replication support) |
| Azure SQL Database as subscriber | Yes (push subscription from on-prem publisher) |
| Azure SQL MI as distributor | Yes |
| Merge replication to Azure SQL DB | No |
-- Create a push subscription to Azure SQL Database from an on-prem publisher
EXEC sp_addsubscription
@publication = 'MyPublication',
@subscriber = 'yourserver.database.windows.net',
@destination_db = 'YourAzureSQLDB',
@subscription_type = 'Push',
@sync_type = 'automatic';
EXEC sp_addpushsubscription_agent
@publication = 'MyPublication',
@subscriber = 'yourserver.database.windows.net',
@subscriber_db = 'YourAzureSQLDB',
@subscriber_security_mode = 0, -- SQL auth required for Azure SQL DB
@subscriber_login = 'repl_user',
@subscriber_password = 'StrongPassword!1';Remove in reverse order: subscriptions first, then articles, then publication, then distribution configuration.
-- 1. Remove push subscription
EXEC sp_dropsubscription
@publication = 'MyPublication',
@article = 'all',
@subscriber = 'SubscriberServerName';
-- 2. Drop the publication
EXEC sp_droppublication
@publication = 'MyPublication';
-- 3. Disable publishing on the database
EXEC sp_replicationdboption
@dbname = 'YourPublisherDB',
@optname = 'publish',
@value = 'false';
-- 4. Remove the distributor (if no other publishers)
EXEC sp_dropdistributiondb
@database = 'distribution';
EXEC sp_dropdistributor
@no_checks = 1; -- use only if you're sure nothing else uses this distributor[!WARNING] Nuclear option If replication is in a broken state and normal cleanup fails, use:
EXEC sp_removedbreplication @dbname = 'YourPublisherDB';This removes ALL replication metadata from the database. Use only as a last resort — it does not clean up the distribution database or subscriber metadata.
-- List all publications on this server
SELECT * FROM sys.publications; -- publisher DB context
SELECT * FROM distribution.dbo.MSpublications;
-- List all articles for a publication
SELECT
art.article,
art.source_object,
art.destination_object,
art.filter,
art.filter_clause,
art.status
FROM distribution.dbo.MSarticles art
JOIN distribution.dbo.MSpublications pub ON art.pubid = pub.pubid
WHERE pub.publication = 'MyPublication';
-- List all subscriptions and their status
SELECT
srv.srvname AS subscriber,
sub.dest_db,
pub.publication,
CASE sub.status
WHEN 0 THEN 'inactive'
WHEN 1 THEN 'subscribed'
WHEN 2 THEN 'active'
END AS status,
sub.subscription_type
FROM distribution.dbo.MSsubscriptions sub
JOIN distribution.dbo.MSarticles art ON sub.artid = art.artid
JOIN distribution.dbo.MSpublications pub ON art.pubid = pub.pubid
JOIN master.sys.servers srv ON sub.srvid = srv.srvid;
-- Check undistributed command count per subscriber (replication latency proxy)
SELECT
a.subscriber,
a.subscriber_db,
a.publication,
b.undistrib_cmds,
b.avgdelay
FROM distribution.dbo.MSdistribution_status b
JOIN distribution.dbo.MSsubscriptions a ON b.agent_id = a.agent_id
ORDER BY b.undistrib_cmds DESC;
-- Replication agent job names
SELECT
j.name AS job_name,
j.enabled,
ja.last_run_date,
ja.last_run_time,
ja.last_run_outcome
FROM msdb.dbo.sysjobs j
JOIN msdb.dbo.sysjobactivity ja ON j.job_id = ja.job_id
WHERE j.name LIKE '%repl%'
AND ja.session_id = (SELECT MAX(session_id) FROM msdb.dbo.sysjobactivity);
-- Check if replicate_ddl is on
SELECT
publication,
replicate_ddl,
retention,
allow_push,
allow_pull
FROM distribution.dbo.MSpublications;
-- Tracer token latency history
SELECT
tt.tracer_id,
tt.publisher_commit,
th.subscriber,
th.subscriber_commit,
DATEDIFF(ms, tt.publisher_commit, th.subscriber_commit) AS latency_ms
FROM distribution.dbo.MSpublisher_tokens tt
LEFT JOIN distribution.dbo.MSsubscriber_info si ON 1=1
JOIN distribution.dbo.MStransactions_history th ON tt.tracer_id = th.tracer_id
ORDER BY tt.publisher_commit DESC;Log truncation held by Log Reader. If the Log Reader Agent stops (or lags), the publisher transaction log cannot be truncated at the oldest unreplicated LSN. Monitor log_reuse_wait_desc and agent status together. An unmonitored publication can grow the log to capacity overnight.
replicate_ddl doesn't cover everything. sp_rename, partition-related DDL, and full-text index DDL do not replicate even with replicate_ddl = 1. Treat these as manual operations.
IDENTITY range management. When replicating tables with IDENTITY columns, SQL Server manages non-overlapping identity ranges at publisher vs subscriber. If ranges run out, inserts at the subscriber fail. Monitor with sp_showpendingchanges and MSreplication_objects.
Subscription expiry is silent until it's a problem. The default @retention is 336 hours (14 days). A subscriber that is inactive (e.g., a dev box) for more than 14 days becomes expired and must be re-initialized. Increase retention for non-critical subscribers, but a higher value means the distribution database retains more data.
Snapshot share permissions. The Snapshot Agent service account needs Write access to the snapshot share; the Distribution Agent service account needs Read access. When agents run as different accounts (common in production), this is a frequent setup failure.
Subscriber cannot have FK constraints pointing to un-replicated tables. If you replicate a child table but not the parent, FK constraints at the subscriber will block the Distribution Agent. Either replicate both tables (and order them correctly) or disable FK constraints at the subscriber.
Filtered articles and joins. If you use a row filter that references a join filter (sp_addmergefilter / join filter in transactional via sp_articleview), all joined tables must also be articles in the same publication.
P2P conflict detection doesn't resolve. When a conflict is detected in P2P replication, the transaction is marked in error and the agent stops. You must resolve it manually (delete the conflicting row on one side, restart the agent). Unlike merge replication, there is no automatic conflict resolution.
Replication and TDE. If the publisher database uses TDE, the distribution database does not need TDE — the changes are stored in plain text in the distribution database. This can be a compliance concern; protect the distributor accordingly.
sp_removedbreplication as emergency exit. This stored procedure removes all replication metadata from the calling database context but does NOT clean up the distribution database. Run it on both publisher and subscriber databases, then manually clean up distribution..MSarticles, MSsubscriptions, etc., or drop and recreate the distribution database.
Replication monitor lag metric is based on tracer tokens. The latency displayed in Replication Monitor is only updated when a tracer token is inserted. By default, tokens are inserted every 5 minutes. Instant high latency is an estimate; use sp_posttracertoken manually for accurate measurement.
Adding a publication recompiles the Log Reader. The Log Reader Agent scans the transaction log looking for marked transactions. Adding or removing articles forces a restart of the Log Reader, which causes a brief gap in delivery. Coordinate with low-traffic windows.
43-high-availability.md — Always On AGs as an alternative to replication for HA/DR37-change-tracking-cdc.md — CDC for change data capture without replication overhead40-service-broker-queuing.md — SSB for async messaging within/across databases44-backup-restore.md — initializing replication from backup (sync_type = 'initialize with backup')50-sql-server-agent.md — SQL Agent jobs that drive replication agentsreferences