Data types
PostgreSQL to SQL Server data types
Every PostgreSQL data type that SQL Server stores differently, what the converter writes for it, when, and the report line.
Each row is a type from PostgreSQL that SQL Server stores differently: what the SQL Server script gets, why, and the line the report writes about it. The rows come from the converter's own rules, so this page matches what it does.
Convert in the browser: PostgreSQL to SQL Server opens the converter set up for this pair. The PostgreSQL to SQL Server migration guide covers dumping, loading and the rules for keys, indexes and names.
A report line is a fix when the converter changed something and lists it, or a problem when a value or object is worth a look.
Numbers
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| numeric(p,s) past 38 digits; numeric without a precision | nvarchar; decimal(p,s) fitted to the values when they fit 38 digits | SQL Server's decimal holds 38 digits. A numeric without a precision gets the narrowest decimal its values fit; wider numbers are kept as text so every digit stays exact. | decimal_as_text (fix) |
| money | decimal(19,2) | SQL Server's money is smaller than PostgreSQL's and has four decimals; decimal(19,2) holds every amount, without the currency symbol the server's locale added. | type_replaced (fix) |
| oid, xid, cid | bigint with a CHECK that it isn't negative | bigint holds every value. | None |
Booleans and bits
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| boolean | bit | bit holds true as 1 and false as 0. | None |
| bit(n), bit varying | bit for one bit; bigint up to 63 bits; text digits beyond | A single bit is a yes or no; longer bit strings are stored as the number their bits make. | None |
Text
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| varchar(n), char(n), text | nvarchar(n); text as nvarchar(max), or nvarchar(450) when a key uses it and every value fits | nvarchar holds every character. SQL Server can't index nvarchar(max), so text in a key is sized to fit a key. | None |
| text that isn't valid in its encoding | the bad bytes as the replacement character | nvarchar holds Unicode text. | invalid_utf8_replaced (problem) |
Enums and sets
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| enum types | nvarchar with a CHECK of its labels | Text with a check keeps the same values. | None |
| enum columns holding values outside their labels | nvarchar without a check | Values the labels don't name would fail the check, so the check isn't created. | enum_check_skipped (problem) |
Dates and times
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| interval | nvarchar(100) with PostgreSQL's text | SQL Server has no interval type; the text keeps every part. | type_replaced (fix) |
| timestamp | datetime2 | A timestamp without time zone is a wall-clock reading, like datetime2. | None |
| timestamp with time zone | datetimeoffset in UTC | datetimeoffset keeps the moment; each value is written in UTC, the same instant whatever offset the dump wrote. | None |
| time with 24:00:00 in it | nvarchar(17) | SQL Server's time ends at 23:59:59.9999999, so a column holding 24:00:00 keeps its text. | time_as_interval (problem) |
| infinity, -infinity and BC dates | NULL | SQL Server stores years 1 to 9999 without infinity. Those values become NULL and the report counts them. | date_out_of_range (problem) |
Binary
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| bytea | varbinary(max) | Bytes stay bytes. | None |
JSON, spatial and other types
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| arrays (integer[], text[]...) | nvarchar(max) with a JSON array | SQL Server has no arrays. A JSON array keeps every item and NULL, and OPENJSON reads it. | type_replaced (fix) |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | nvarchar(max) with PostgreSQL's text | SQL Server has no equivalent type, so each value keeps the text PostgreSQL wrote. | type_replaced (fix) |
| json, jsonb | nvarchar(max) | SQL Server's JSON functions read JSON from text. | None |
| NaN and Infinity in numeric, real and double precision | NULL | SQL Server's numbers have no NaN or Infinity, so those values become NULL and the report counts them. | invalid_value_null (problem) |
| uuid, inet, cidr, macaddr | uniqueidentifier; addresses as nvarchar | uniqueidentifier is SQL Server's UUID; addresses are kept as text. | None |
| types IntoSQL doesn't know | nvarchar(max) | Text holds any value; change the type after loading. | unknown_type (problem) |
Counters and identities
| In PostgreSQL | In SQL Server | Why | Report line |
|---|---|---|---|
| serial, identity | IDENTITY, continuing after the highest id | The copied ids load with IDENTITY_INSERT, and the counter continues after the highest one or the sequence's next value, whichever is higher. | None |
| a second serial or identity column in a table | a plain integer | SQL Server counts one IDENTITY column per table; any other keeps its values without a counter. | counter_dropped (problem) |
Other pairs
- PostgreSQL into PostgreSQL, MySQL, MariaDB, SQLite, Oracle
- Into SQL Server from MySQL, MariaDB, SQL Server, SQLite, Oracle
- Every pair