Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when: comparing SQL Server tables across instances, data migration validation, ETL verification, row mismatch detection, schema drift, reconciliation report, production vs staging comparison. Uses mssql-python driver with Apache Arrow for fast columnar data transfer and comparison.
.claude/skills/sql-server-table-reconciliation/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 96% | 50 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-18 | ✗→✓ | ▲ Improved | — | — |
| case-13 | ✗→✓ | ▲ Improved | — | — |
| case-06 | ✗→✓ | ▲ Improved | — | — |
| case-09 | ✗→✓ | ▲ Improved | — | — |
Compare identical tables across two SQL Server instances using Python with mssql-python driver and Apache Arrow. Detect missing rows, column mismatches, schema drift, and produce a reconciliation report.
| Parameter | Required | Description | |-----------|----------|-------------| | Source server | Yes | Source SQL Server (e.g. prod-server.database.windows.net) | | Source database | Yes | Source database name | | Target server | Yes | Target SQL Server (e.g. staging-server.database.windows.net) | | Target database | Yes | Target database name | | Tables | Yes | Comma-separated schema.table names, or schema.* wildcard (e.g. dbo.Orders,dbo.Items or dbo.*) | | Auth mode | Yes | sql (user/password) or entra (Azure AD/token) | | Primary key | Auto-detect | Column(s) forming the row identity. Auto-detect from metadata if not provided. | | Columns to compare | All | Subset of columns, or all non-PK columns | | Chunk size | 100000 | Rows per batch for large tables | | Output format | console | console, csv, parquet, or json |
The reconciliation logic is provided as a standalone script at scripts/reconcile.py. Invoke it with the appropriate arguments based on user inputs:
bashpython scripts/reconcile.py \ --source-server <source_server> \ --source-database <source_database> \ --target-server <target_server> \ --target-database <target_database> \ --tables "<table_spec>" \ --auth <sql|entra> \ --chunk-size <chunk_size> \ --output <console|csv|json>
| Argument | Description | |----------|-------------| | --primary-key | Comma-separated PK column(s). Omit to auto-detect. | | --columns | Comma-separated columns to compare. Omit to compare all non-PK columns. |
Single table with SQL auth:
bashpython scripts/reconcile.py \ --source-server prod-server.database.windows.net \ --source-database ProdDB \ --target-server staging-server.database.windows.net \ --target-database StagingDB \ --tables "dbo.Orders" \ --auth sql \ --output console
Wildcard with Entra auth and CSV output:
bashpython scripts/reconcile.py \ --source-server prod-server.database.windows.net \ --source-database ProdDB \ --target-server staging-server.database.windows.net \ --target-database StagingDB \ --tables "dbo.*" \ --auth entra \ --output csv
Install required packages before running:
bashpip install mssql-python pyarrow pandas
NULL == NULL is considered a match (both sides missing = no diff)OFFSET/FETCH or ROW_NUMBER() partitioningWhen table has >1M rows, generate a hash pre-check:
sqlSELECT {pk_cols}, HASHBYTES('SHA2_256', CONCAT_WS('|', col1, col2, ...)) AS row_hash FROM {table}
Compare hashes first; only fetch full rows for mismatched hashes. This reduces data transfer significantly.
Reconciling dbo.EMPLOYEES...
Reconciling dbo.DEPARTMENTS...
Reconciling dbo.JOBS...
--- dbo.EMPLOYEES ---
Source: 107 Target: 107
Missing: 0 Extra: 0 Mismatches: 0
Result: ✓ IDENTICAL
--- dbo.DEPARTMENTS ---
Source: 27 Target: 27
Missing: 0 Extra: 0 Mismatches: 3
Result: ✗ DIFFERENCES FOUND
--- dbo.JOBS ---
Source: 19 Target: 19
Missing: 0 Extra: 0 Mismatches: 0
Result: ✓ IDENTICAL
=== Summary: 2 passed, 1 failed, 0 skipped / 3 tables ===When a single table is provided, include full detail (schema drift, sample rows, mismatches). When multiple tables, use the compact per-table format above with full detail only for tables with FAIL status.
| Scenario | Strategy | |----------|----------| | < 100K rows | Single Arrow fetch, in-memory pandas compare | | 100K–1M rows | Chunked extraction (100K batches), streaming comparison | | > 1M rows | Hash pre-check → only fetch mismatched rows | | Wide tables (100+ cols) | Compare PK + hash first, drill into specific columns on mismatch | | Network-constrained | Use Arrow columnar format (10-50x smaller than row-by-row) |
mssql-python driver (not pyodbc, pymssql)cursor.arrow()) for data extractionencrypt=True throw errors)os.environ / getpass (env vars: MSSQL_USER, MSSQL_PASSWORD)? placeholders) for metadata lookups — never f-string interpolate user input into SQL| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.