T-SQL to PostgreSQL DDL Converter

Convert SQL Server CREATE TABLE statements to PostgreSQL DDL using a documented type mapping. Warnings list everything changed, dropped or left unmapped.

Private by design: everything you enter is processed in your browser. It is not sent to our servers.

Type and function mapping used (with documentation links)
T-SQLPostgreSQLDocsNote
bigintbigintMicrosoft · PostgreSQL
int, integerintegerMicrosoft · PostgreSQL
smallintsmallintMicrosoft · PostgreSQL
tinyintsmallintMicrosoft · PostgreSQLPostgreSQL has no 1-byte integer; smallint covers tinyint’s 0–255 range.
bitbooleanMicrosoft · PostgreSQLbit values 0/1 become false/true.
decimal, numeric, decnumeric({p},{s})Microsoft · PostgreSQL
moneynumeric(19,4)Microsoft · PostgreSQLnumeric(19,4) matches money’s scale exactly and avoids the locale-dependent PostgreSQL money type.
smallmoneynumeric(10,4)Microsoft · PostgreSQL
floatdouble precisionMicrosoft · PostgreSQLfloat(n) with n ≤ 24 becomes real; otherwise double precision.
realrealMicrosoft · PostgreSQL
datedateMicrosoft · PostgreSQL
timetime({n})Microsoft · PostgreSQL
datetimetimestamp(3)Microsoft · PostgreSQLdatetime is accurate to increments of .000/.003/.007 s, so millisecond precision is kept.
smalldatetimetimestamp(0)Microsoft · PostgreSQLsmalldatetime has minute accuracy.
datetime2timestamp({n})Microsoft · PostgreSQL
datetimeoffsettimestamp({n}) with time zoneMicrosoft · PostgreSQLPostgreSQL stores the instant in UTC; the original offset is not kept.
char, nchar, characterchar({n})Microsoft · PostgreSQL
varchar, nvarcharvarchar({n})Microsoft · PostgreSQL(MAX) becomes text.
text, ntexttextMicrosoft · PostgreSQL
binary, varbinary, imagebyteaMicrosoft · PostgreSQLPostgreSQL bytea has no fixed length; length limits are not carried over.
uniqueidentifieruuidMicrosoft · PostgreSQL
xmlxmlMicrosoft · PostgreSQL
rowversion, timestampbyteaMicrosoft · PostgreSQLT-SQL rowversion is an automatically maintained counter; PostgreSQL has no equivalent column type, so the value is NOT maintained.
default getdate()CURRENT_TIMESTAMPMicrosoft · PostgreSQL
default sysdatetime()CURRENT_TIMESTAMPMicrosoft · PostgreSQL
default getutcdate()(now() AT TIME ZONE 'utc')Microsoft · PostgreSQL
default sysutcdatetime()(now() AT TIME ZONE 'utc')Microsoft · PostgreSQL
default newid()gen_random_uuid()Microsoft · PostgreSQL
default newsequentialid()gen_random_uuid()Microsoft · PostgreSQL

How to use

  1. Paste one or more CREATE TABLE statements written for SQL Server (T-SQL). Comments, GO separators and bracketed names are fine.
  2. Choose how names are converted (snake_case, lower case, or preserved in quotes), whether IDENTITY becomes GENERATED BY DEFAULT or ALWAYS, and whether the dbo schema becomes public.
  3. Read the PostgreSQL DDL and then the warnings list. Every warning describes something that was changed, dropped or left for you to review.
  4. Copy or download the .sql file and run it against a test database before using it.

Worked example

A typical SQL Server table with an identity key, a GUID, nullable text, a bit flag, a decimal and a creation timestamp.

T-SQL input

CREATE TABLE [dbo].[CustomerOrder] (
    [OrderId] INT IDENTITY(1,1) NOT NULL,
    [CustomerGuid] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
    [Notes] NVARCHAR(MAX) NULL,
    [Title] NVARCHAR(100) NOT NULL,
    [IsPaid] BIT NOT NULL DEFAULT ((0)),
    [Total] DECIMAL(10,2) NOT NULL,
    [CreatedAt] DATETIME2(7) NOT NULL DEFAULT (GETDATE()),
    CONSTRAINT [PK_CustomerOrder] PRIMARY KEY CLUSTERED ([OrderId] ASC)
) ON [PRIMARY];

Result

Example result
Warning 1Storage clauses after the column list (WITH, ON [PRIMARY], TEXTIMAGE_ON) were ignored.
Warning 2Fractional-second precision 7 exceeds PostgreSQL's maximum of 6; 6 was used (column "CreatedAt").

PostgreSQL output

CREATE TABLE public.customer_order (
    order_id integer GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    customer_guid uuid NOT NULL DEFAULT gen_random_uuid(),
    notes text,
    title varchar(100) NOT NULL,
    is_paid boolean NOT NULL DEFAULT false,
    total numeric(10,2) NOT NULL,
    created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT pk_customer_order PRIMARY KEY (order_id)
);

How it works

The converter tokenizes the T-SQL, finds each CREATE TABLE, splits the column list at top-level commas and converts every column and constraint with a fixed mapping table; there is no AI involved and the same input always gives the same output.

Types follow the mapping table on this page, each row of which links the Microsoft page for the SQL Server type and the PostgreSQL page for the target type. For example NVARCHAR(MAX) becomes text, DATETIME2(n) becomes timestamp(n), BIT becomes boolean, UNIQUEIDENTIFIER becomes uuid and TINYINT becomes smallint because PostgreSQL has no one-byte integer.

IDENTITY(seed, increment) becomes GENERATED BY DEFAULT AS IDENTITY (or ALWAYS, if you choose) with START WITH and INCREMENT BY when they differ from 1. BY DEFAULT is the default because data migrations usually need to load existing ids; ALWAYS rejects explicit values unless OVERRIDING SYSTEM VALUE is used.

Defaults: GETDATE() and SYSDATETIME() become CURRENT_TIMESTAMP, GETUTCDATE() becomes (now() AT TIME ZONE 'utc'), NEWID() becomes gen_random_uuid(), ((0)) on a bit column becomes false. Anything else is copied unchanged with a warning. Computed columns become generated columns: PERSISTED → STORED, otherwise VIRTUAL (PostgreSQL 18 or later).

Reserved words are quoted using the list of reserved key words from the PostgreSQL manual, so a column named User or Order becomes "user" or "order".

Common errors

No CREATE TABLE statement was found.
The input only contains other statements. Only CREATE TABLE is converted.
Unbalanced parentheses.
A parenthesis is missing or extra in your CREATE TABLE; check the end of the column list.
Unterminated 'string'.
A quoted string, [bracketed name] or comment is not closed.
Unknown type "…" (column "…") was copied unchanged. Check it exists in PostgreSQL.
The type is not in the mapping table. Replace it with the PostgreSQL type you want.
Type "geography" (column "…") has no automatic PostgreSQL equivalent; it was left as a commented placeholder.
Spatial, hierarchyid and sql_variant types need a manual choice (for example PostGIS).
Default for column "…" (…) was copied unchanged; check that it is valid PostgreSQL.
Rewrite the default using PostgreSQL syntax or functions.

Limits

  • Only CREATE TABLE is converted. Indexes, views, stored procedures, triggers, sequences, ALTER statements and data are not.
  • Expressions in CHECK constraints, computed columns and unrecognised defaults are copied verbatim; T-SQL functions in them will fail in PostgreSQL until you rewrite them.
  • Collations, filegroups, compression, sparse columns, rowversion behaviour and clustered/non-clustered index choices have no direct equivalent and are dropped with a warning.
  • datetimeoffset keeps only the instant, not the offset; fractional-second precision above 6 is reduced to 6.
  • Always review the output and test it. The converter does not connect to any database.

Privacy

Your SQL is converted in your browser and never sent anywhere. Share links include the SQL only if it is short. No account is needed, no cookies are set and no analytics run. See thePrivacy Policy. The “Copy share link” button puts your settings after the “#” in the address; browsers do not send that part to servers, and the page’s canonical address never includes it.

FAQ

Why snake_case names by default?

PostgreSQL folds unquoted names to lower case, so CustomerOrder would become customerorder. snake_case keeps names readable without quoting. Choose “preserve” to keep exact case in double quotes.

Why is TINYINT smallint?

PostgreSQL has smallint (2 bytes), integer and bigint but no one-byte integer type.

Why does MONEY become numeric(19,4)?

It matches the fixed four decimal places without relying on the PostgreSQL money type, whose formatting depends on locale settings.

Does it migrate data?

No. It converts table definitions only.

Is NEWID() the same as gen_random_uuid()?

Both generate unique identifiers. NEWSEQUENTIALID() is the SQL Server function that generates ordered values; gen_random_uuid() is not ordered, so index behaviour can differ if you relied on sequential ids.

Embed this tool

Paste this snippet into your own page or docs to show the tool inline. The embedded view works the same way (everything runs in the visitor’s browser) and carries a small link back to MakeTools.

Sources

Page last updated 2026-10-14. Written by Prajyot Mulik; see the editorial policy.