Skip to content

Commit 2ae8ccd

Browse files
sql data migration verification script (#1049)
Co-authored-by: midhun-aot <105463561+midhun-aot@users.noreply.github.com>
1 parent 4470f83 commit 2ae8ccd

File tree

7 files changed

+1547
-0
lines changed

7 files changed

+1547
-0
lines changed

migration_verification/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# compiled output
2+
/dist
3+
/node_modules
4+
/build
5+
6+
# dotenv environment variable files
7+
.env
8+
.env.development.local
9+
.env.test.local
10+
.env.production.local
11+
.env.local

migration_verification/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Introduction
2+
3+
This code can be used to verify the count of records in each table once the data is migrated from SQL Server to Postgres.
4+
5+
6+
## How to run
7+
8+
### Usage:
9+
10+
Please make sure the source and destination connection string are updated in the .env before running the script.
11+
12+
Run the script from the command prompt with:
13+
14+
```
15+
npx ts-node compareData.ts
16+
17+
```
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import * as dotenv from 'dotenv';
2+
dotenv.config();
3+
import * as sql from 'mssql';
4+
import type { config } from 'mssql';
5+
const { Client } = require('pg');
6+
7+
// --- SQL Server (NTLM) Config ---
8+
const sqlServerConfig: config = {
9+
server: process.env.SQL_SERVER || 'localhost',
10+
database: process.env.SQL_DB,
11+
port: Number(process.env.SQL_PORT),
12+
user: process.env.SQL_USER, // NTLM user
13+
password: process.env.SQL_PASSWORD, // NTLM password
14+
domain: process.env.SQL_DOMAIN, // important for NTLM
15+
options: {
16+
encrypt: false,
17+
trustServerCertificate: true,
18+
enableArithAbort: true,
19+
}
20+
};
21+
22+
// --- PostgreSQL Config ---
23+
const pgClient = new Client({
24+
user: process.env.PG_USER,
25+
host: process.env.PG_HOST || 'localhost',
26+
database: process.env.PG_DB,
27+
password: process.env.PG_PASSWORD,
28+
port: Number(process.env.PG_PORT) || 5432,
29+
});
30+
31+
// --- Helper: PascalCase → snake_case ---
32+
function toSnakeCase(str: string): string {
33+
return str
34+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
35+
.replace(/([A-Z])([A-Z][a-z])/g, '$1_$2')
36+
.toLowerCase();
37+
}
38+
39+
async function testConnection() {
40+
try {
41+
const pool = await sql.connect(sqlServerConfig);
42+
const result = await pool.request().query('SELECT GETDATE() AS now');
43+
console.log('Connected! Current time:', result.recordset[0].now);
44+
await pool.close();
45+
} catch (err) {
46+
console.error('Connection failed:', err);
47+
}
48+
}
49+
50+
async function compareAllTableRowCounts(): Promise<void> {
51+
const pool = new sql.ConnectionPool(sqlServerConfig);
52+
try {
53+
await pool.connect();
54+
await pgClient.connect();
55+
56+
// Get all SQL Server table names
57+
const tableQuery = `
58+
SELECT TABLE_NAME
59+
FROM INFORMATION_SCHEMA.TABLES
60+
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = 'dbo'
61+
`;
62+
63+
const result = await pool.request().query(tableQuery);
64+
const tableNames: string[] = result.recordset.map((row: any) => row.TABLE_NAME);
65+
66+
//console.log('tableNames', tableNames);
67+
console.log(`\n${'SQL Table'.padEnd(30)} ${'Postgres Table'.padEnd(30)} SQL Count PG Count Match`);
68+
console.log('-'.repeat(85));
69+
70+
let pgTableNotFound = [];
71+
let sqlTableNotFound = [];
72+
for (const sqlTable of tableNames) {
73+
const pgTable = toSnakeCase(sqlTable);
74+
let sqlCount;
75+
let pgCount;
76+
//console.log('pgTable', pgTable);
77+
try {
78+
const sqlRes = await pool.request().query(`SELECT COUNT(*) AS count FROM [${sqlTable}]`);
79+
sqlCount = Number(sqlRes.recordset[0].count);
80+
} catch (e) {
81+
sqlTableNotFound.push(pgTable);
82+
//console.warn(`Could not query SQL Server table: ${sqlTable}`);
83+
}
84+
85+
try {
86+
const pgRes = await pgClient.query(`SELECT COUNT(*) FROM cats."${pgTable}"`);
87+
pgCount = Number(pgRes.rows[0].count);
88+
} catch (e) {
89+
pgTableNotFound.push(pgTable);
90+
//console.warn(`Could not query Postgres table: ${pgTable}`);
91+
}
92+
93+
94+
const match = sqlCount === pgCount ? '✅' : '❌';
95+
//if (sqlCount !== pgCount)
96+
console.log(`${sqlTable.padEnd(30)} ${pgTable.padEnd(30)} ${String(sqlCount).padEnd(10)} ${String(pgCount).padEnd(10)} ${match}`);
97+
}
98+
99+
if (sqlTableNotFound.length > 0) {
100+
console.log('-'.repeat(85));
101+
console.log('Table not in Oracle:');
102+
console.log('-'.repeat(85));
103+
sqlTableNotFound.forEach((table) => {
104+
console.log(table);
105+
});
106+
}
107+
108+
if (pgTableNotFound.length > 0) {
109+
console.log('-'.repeat(85));
110+
console.log('Table not in Postgres:');
111+
console.log('-'.repeat(85));
112+
pgTableNotFound.forEach((table) => {
113+
console.log(table);
114+
});
115+
}
116+
} catch (err) {
117+
console.error('🚨 Connection or query failed:', err);
118+
} finally {
119+
await pool.close(); // Close the SQL Server connection pool properly
120+
await pgClient.end(); // Close Postgres client connection
121+
}
122+
}
123+
124+
125+
compareAllTableRowCounts();

0 commit comments

Comments
 (0)