| Keys and identity |
SERIAL, IDENTITY |
A PostgreSQL key is fed by a sequence that exists as an object in its own right; SQL Server attaches the generator to the column. |
Created as an IDENTITY column and reseeded with DBCC CHECKIDENT above the highest loaded value, so the next insert does not collide with a migrated row. |
uuid |
Nothing to reconcile - both engines have a native GUID type. |
Becomes UNIQUEIDENTIFIER, 16 bytes and comparable as a GUID rather than as text. |
| Numbers |
boolean |
PostgreSQL has a real boolean; SQL Server's nearest type is a one-bit integer. |
Becomes BIT, which every SQL Server client reads back as true/false. |
double precision, real |
The names line up, but only if the width is respected - double precision is eight bytes and real is four. |
double precision becomes FLOAT and real becomes REAL, so neither loses half its significant digits on the way. |
numeric, money |
PostgreSQL's money renders according to the server's locale, which is not something to carry into another engine. |
numeric(p,s) keeps its precision and scale as NUMERIC(p,s); money becomes SQL Server MONEY. |
bit(n), bit varying(n) |
SQL Server's BIT holds a single bit, so a bit string has nowhere to go as a number. |
Stored as BINARY(n) or VARBINARY(n) sized from the declaration, keeping every bit rather than the first one. |
| Date and time |
date, time(n), timestamp(n) |
The legacy SQL Server DATETIME rounds to 3.33 ms and has no date-only or time-only form. |
Each keeps its own shape: DATE, TIME(n), and DATETIME2(n) with the declared scale, so a date does not grow a midnight and the microseconds survive. |
timestamptz |
The value is an instant; SQL Server's plain timestamp types carry no zone at all. |
Becomes DATETIMEOFFSET, which keeps the offset with the value instead of leaving the instant to be inferred. |
| Text and documents |
varchar, char, text |
A UTF8 PostgreSQL database holds anything; a SQL Server VARCHAR holds only what the destination collation's code page covers. |
All three become NVARCHAR, NCHAR and NVARCHAR(MAX), so Cyrillic, CJK and emoji arrive intact whatever the target collation is. |
json, jsonb |
SQL Server has no JSON column type; it validates and queries JSON held as text. |
Both become NVARCHAR(MAX), which is what ISJSON, JSON_VALUE and OPENJSON read. The jsonb indexing is a PostgreSQL feature and does not travel. |
xml |
Nothing to reconcile - SQL Server has its own XML type. |
Stays XML, so the document remains queryable rather than becoming opaque text. |
| Binary |
bytea |
Both engines store raw bytes, but SQL Server's older large-object type is deprecated. |
Becomes VARBINARY(MAX). |
| PostgreSQL-only types |
Arrays, ranges, ENUM, interval, inet, cidr |
SQL Server has no equivalent for any of these. There is no mapping that keeps the behaviour - only ones that keep the value. |
Each is written as NVARCHAR holding its text form: {1,2,3}, [10,20), the label, 1 year 2 mons, 192.168.1.0/24. Nothing is dropped, and the column stays readable and queryable as text. |
| Structure |
| Schemas and names |
PostgreSQL folds unquoted names to lower case and reaches objects through search_path; SQL Server has neither behaviour. |
Schemas map onto SQL Server schemas, dbo by default, and quoted mixed-case names are normalised or preserved in bracket form - you pick before the load. |
| Indexes and constraints |
Partial and expression indexes exist on both sides but are spelled differently; GiST, GIN and exclusion constraints have no counterpart. |
Partial and expression indexes move to filtered indexes or computed-column patterns where the semantics match. The rest are flagged in the review rather than dropped silently. |