Rapprochement de tables SQL Server
Comparez des tables identiques sur deux instances SQL Server en utilisant Python avec le driver mssql-python et Apache Arrow. Détectez les lignes manquantes, les incompatibilités de colonnes, la dérive de schéma et générez un rapport de rapprochement.
Workflow
- Collecter les détails de connexion pour la source et la cible
- Identifier la clé primaire / clé composite
- Détecter les différences de schéma
- Extraire les données via Arrow pour un transfert columnar efficace
- Comparer les lignes et colonnes
- Générer un rapport de rapprochement
Collecter les entrées
| Paramètre | Requis | Description |
|---|---|---|
| Serveur source | Oui | SQL Server source (ex. prod-server.database.windows.net) |
| Base de données source | Oui | Nom de la base de données source |
| Serveur cible | Oui | SQL Server cible (ex. staging-server.database.windows.net) |
| Base de données cible | Oui | Nom de la base de données cible |
| Tables | Oui | Noms schema.table séparés par des virgules, ou wildcard schema.* (ex. dbo.Orders,dbo.Items ou dbo.*) |
| Mode d'auth | Oui | sql (utilisateur/mot de passe) ou entra (Azure AD/token) |
| Clé primaire | Détection auto | Colonne(s) formant l'identité de la ligne. Détection auto à partir des métadonnées si non fourni. |
| Colonnes à comparer | Toutes | Sous-ensemble de colonnes, ou toutes les colonnes non-PK |
| Taille de chunk | 100000 |
Lignes par batch pour les grandes tables |
| Format de sortie | console |
console, csv, parquet, ou json |
Script fourni
La logique de rapprochement est fournie sous forme de script autonome à scripts/reconcile.py. Invoquez-le avec les arguments appropriés selon les entrées utilisateur :
python 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>
Arguments optionnels
| Argument | Description |
|---|---|
--primary-key |
Colonnes PK séparées par des virgules. Omettez pour la détection auto. |
--columns |
Colonnes séparées par des virgules à comparer. Omettez pour comparer toutes les colonnes non-PK. |
Exemples d'invocation
Table unique avec auth SQL :
python 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 avec auth Entra et sortie CSV :
python 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
Prérequis
Installez les packages requis avant d'exécuter :
pip install mssql-python pyarrow pandas
Règles de comparaison
- Normaliser les types avant comparaison : convertir les décimales à la même précision, trimmer les chaînes, normaliser datetime en UTC
- Gestion des NULL :
NULL == NULLest considéré comme une correspondance (les deux côtés manquants = pas de diff) - Ignorer l'ordre des lignes : toujours comparer par jonction PK, jamais positionnellement
- Grandes tables : extraction par chunks avec
OFFSET/FETCHou partitionnementROW_NUMBER()
Optimisation basée sur hash (pour grandes tables)
Quand la table a >1M lignes, générez un pré-check hash :
SELECT {pk_cols},
HASHBYTES('SHA2_256', CONCAT_WS('|', col1, col2, ...)) AS row_hash
FROM {table}
Comparez d'abord les hashs ; récupérez les lignes complètes uniquement pour les hashs mal appariés. Cela réduit significativement le transfert de données.
Format du rapport
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 ===
Quand une table unique est fournie, incluez le détail complet (dérive de schéma, exemples de lignes, incompatibilités). Avec plusieurs tables, utilisez le format compact par table ci-dessus avec le détail complet uniquement pour les tables avec statut FAIL.
Considérations de performance
| Scénario | Stratégie |
|---|---|
| < 100K lignes | Extraction Arrow unique, comparaison pandas en mémoire |
| 100K–1M lignes | Extraction par chunks (batches 100K), comparaison en streaming |
| > 1M lignes | Pré-check hash → récupérer uniquement les lignes mal appariées |
| Tables larges (100+ cols) | Comparer PK + hash d'abord, approndir colonnes spécifiques en cas de mismatch |
| Réseau contraint | Utiliser format Arrow columnar (10-50x plus petit que ligne par ligne) |
Contraintes
- Toujours utiliser le driver
mssql-python(pas pyodbc, pymssql) - Toujours utiliser Apache Arrow via cursor (
cursor.arrow()) pour l'extraction de données - La connexion DOIT utiliser le format chaîne de connexion, pas les arguments de mot-clé (kwargs comme
encrypt=Truecausent des erreurs) - Ne jamais comparer sans identifier d'abord la PK — demandez à l'utilisateur si la détection auto échoue
- Gérer les défaillances de connexion avec grâce et logique de retry
- Ne jamais coder en dur les credentials dans les scripts générés — utiliser
os.environ/getpass(variables env :MSSQL_USER,MSSQL_PASSWORD) - Ne pas afficher les credentials dans la sortie ou les logs
- Utiliser des requêtes paramétrées (placeholders
?) pour les lookups de métadonnées — ne jamais interpoler l'entrée utilisateur en SQL via f-string