SQL to ER diagram

SQL to ER diagram: paste CREATE TABLE, get a crow's foot diagram

In short

Paste the CREATE TABLE statements from Postgres, MySQL, SQLite or SQL Server and this page draws the entity-relationship diagram: one entity per table, columns with their types, and a crow's-foot relationship for every foreign key. Cardinality is inferred from the constraints — a NOT NULL foreign key is a mandatory one-to-many, a UNIQUE one is one-to-one, and a composite primary key made of two foreign keys marks a many-to-many junction. You also get the schema as Mermaid erDiagram code.

Updates as you type · ⌘/Ctrl+Enter

Diagram

Entities

TableColumnsPrimary keyForeign keys

Relationships

Parent (referenced)Child (holds FK)FK columnConstraintsInferred cardinality

Mermaid erDiagram code

Runs in your browser — the SQL is parsed locally and never uploaded. Only the finished diagram spec (table and column names, no data) is sent to our free image endpoint to draw the picture; nothing is stored.

Updated · by the ChartCraft team

How does the SQL get turned into an ER diagram?

A small SQL parser (not a regular expression) tokenises your script, reads every CREATE TABLE and ALTER TABLE … ADD FOREIGN KEY, and builds a model of tables, columns, keys and references. Each table becomes an entity box; each foreign key becomes a relationship line from the referenced (parent) table to the table that holds the key (child). The crow's-foot marks come from the constraints on the foreign-key column, using the rules in the table below.

  1. Tokenise — identifiers (bare, "quoted", `backticked`, [bracketed]), strings, numbers, punctuation; --, # and /* */ comments are skipped.
  2. Parse statements — column definitions with types, inline constraints, table-level constraints; everything else (CREATE INDEX, INSERT, SET, dialect options such as ENGINE=InnoDB) is skipped and listed in the notes.
  3. Resolve — table-level primary keys and unique constraints are applied to their columns; foreign keys are matched to the referenced tables by name (case-insensitive).
  4. Infer cardinality — from NOT NULL, UNIQUE and composite primary keys (rules below).
  5. Emit — a diagram spec for the free image API, the two tables above, and Mermaid erDiagram code with the matching crow's-foot arrows.

Which SQL syntax does the converter understand?

The common DDL subset shared by Postgres, MySQL/MariaDB, SQLite and SQL Server. Anything outside it is skipped per statement — the rest of the script still converts — and a parse error reports the line and what was expected.

ConstructExampleHow it is read
CREATE TABLE [IF NOT EXISTS] [schema.]name (…)CREATE TABLE IF NOT EXISTS shop.orders (…)One entity named orders; the schema prefix is dropped from the label.
Column with typeprice DECIMAL(10,2) NOT NULLColumn + type as written, including lengths and precision; TIMESTAMP WITH TIME ZONE, INT UNSIGNED, TEXT[] are kept whole.
Inline primary keyid SERIAL PRIMARY KEYMarks the column PK and NOT NULL. Postgres SERIAL/BIGSERIAL, MySQL AUTO_INCREMENT, SQL Server IDENTITY(1,1) and GENERATED … AS IDENTITY are recognised.
Table-level (composite) primary keyPRIMARY KEY (order_id, product_id)Both columns become PK; two FKs covering the whole PK flag the table as a junction (many-to-many).
Inline referenceuser_id INT NOT NULL REFERENCES users(id)Relationship users → orders on user_id. ON DELETE/ON UPDATE, MATCH, DEFERRABLE are accepted and ignored.
Table-level foreign key[CONSTRAINT fk] FOREIGN KEY (a, b) REFERENCES t (x, y)Composite foreign keys are supported; the label lists both columns.
ALTER TABLE … ADD [CONSTRAINT …] FOREIGN KEYALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(id);Merged into the table it targets; also ADD PRIMARY KEY and ADD UNIQUE.
UNIQUE (inline or table-level)email VARCHAR(255) UNIQUE, UNIQUE (a, b)Single-column uniqueness marks the column UK; multi-column uniqueness is only used for cardinality inference.
NOT NULL / NULLmanager_id INT NULLDecides whether the parent side of a relationship is mandatory (||) or optional (|o).
Quoted identifiers"Order Items", `orders`, [dbo].[Users]All three quoting styles; names with spaces are quoted in the Mermaid output.
Comments-- line, # line (MySQL), /* block */Skipped anywhere, including inside column lists.
Dialect tails and optionsENGINE=InnoDB DEFAULT CHARSET=utf8mb4, WITHOUT ROWID, COMMENT 'x', COLLATEIgnored.
DEFAULT expr, CHECK (…), generated columnsstatus VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending','paid'))Skipped with balanced-parenthesis matching, so a comma inside a CHECK does not break the column list.
Secondary indexesKEY idx_name (name), CREATE INDEX …Not part of the ER model — skipped and noted.
Not supportedCREATE TABLE … AS SELECT, CREATE TYPE, views, triggers, stored proceduresSkipped with a note; the error list tells you the line.

How is the cardinality inferred from the constraints?

A foreign key always points from the child row to exactly one parent row at most, so the child side is never "many parents". What the constraints decide is whether that parent is mandatory and whether the parent may have more than one child. The table reads parent → child, the same direction as the Mermaid arrows it produces.

FK column is …Parent sideChild sideInferred relationshipMermaid
NOT NULL, not uniqueexactly one (||)zero or more (o{)one-to-many, parent mandatory — the common caseusers ||--o{ orders
nullable, not uniquezero or one (|o)zero or more (o{)optional one-to-many — the child may exist without a parent (e.g. manager_id on employees)employees |o--o{ employees
NOT NULL + UNIQUE (or the FK is the whole PK)exactly one (||)exactly one (||)one-to-one — each parent has at most one child, each child exactly one parentusers ||--|| profiles
nullable + UNIQUEzero or one (|o)exactly one (||)optional one-to-oneusers |o--|| profiles
composite PK made of exactly two FKs——the table is a junction: a many-to-many between the two referenced tables, drawn as two one-to-many lines plus a noteorders }o--o{ products
no FK at all——no relationship; the entity is drawn standalone—

Strictly, the child-side mark can never be proven from DDL alone: a database does not record that every customer must have at least one order, so the tool never claims a mandatory many (|{). Promote it by hand in the Mermaid code or the studio when your business rule says so. The full notation is on the crow's foot notation page.

What does the Mermaid erDiagram output look like?

Each table becomes an entity block listing type name KEY per column (PK, FK, UK, or combinations such as PK, FK), followed by one relationship line per foreign key, labelled with the FK column. The four-table sample produces this; it pastes straight into GitHub, Notion, Obsidian or the Mermaid live editor.

erDiagram
    users {
        SERIAL id PK
        VARCHAR(255) email UK
        VARCHAR(120) full_name
        TIMESTAMP created_at
    }
    orders {
        SERIAL id PK
        INTEGER user_id FK
        VARCHAR(20) status
        TIMESTAMP placed_at
    }
    order_items {
        INTEGER order_id PK, FK
        INTEGER product_id PK, FK
        INTEGER quantity
        INTEGER unit_cents
    }
    products {
        SERIAL id PK
        VARCHAR(40) sku UK
        VARCHAR(200) name
        INTEGER price_cents
    }
    users ||--o{ orders : "user_id"
    orders ||--o{ order_items : "order_id"
    products ||--o{ order_items : "product_id"
    %% order_items is a junction table: orders }o--o{ products

Mermaid attribute types cannot contain spaces or commas, so DECIMAL(10,2) is written DECIMAL(10_2) and TIMESTAMP WITH TIME ZONE becomes TIMESTAMP_WITH_TIME_ZONE; everything else is verbatim. Symbol meanings are on the ER diagram symbols page.

Questions

Which SQL dialects does the SQL to ER diagram converter support?

PostgreSQL, MySQL and MariaDB, SQLite and SQL Server (T-SQL) DDL all parse, because the converter reads the shared CREATE TABLE grammar and skips dialect-specific tails such as ENGINE=, WITHOUT ROWID or IDENTITY(1,1). Oracle DDL mostly works too as long as it uses standard constraints.

Does my schema leave my browser?

The SQL text never leaves the page: tokenising, parsing and cardinality inference run in JavaScript on your machine. To draw the image, the derived diagram spec (table names, column names and types, relationship types) is sent to ChartCraft's free image endpoint, which renders it and returns an SVG without storing anything. The Mermaid code and the two tables are produced entirely locally.

Why is a relationship drawn one-to-many when I expected one-to-one?

Because the foreign-key column has no UNIQUE constraint. From DDL alone, a non-unique FK can repeat, so several child rows may point to one parent. Add UNIQUE to the FK column (or make it the primary key) and the converter switches to one-to-one, following the rules in the cardinality table.

How are many-to-many relationships detected?

A table whose primary key is composite and consists of exactly two foreign keys — the classic junction such as order_items (order_id, product_id) — is tagged as a junction. The diagram keeps the two one-to-many lines (that is what the database actually has) and adds a Mermaid comment with the equivalent }o--o{ many-to-many line.

What happens if there is a syntax error in my SQL?

The statement that fails is skipped and reported with its line number, column and what the parser expected (for example Line 9, column 48: Expected a data type for column name, found ")"). All the other statements still convert, so one typo does not blank the diagram.

Can it read foreign keys added with ALTER TABLE?

Yes. ALTER TABLE child ADD [CONSTRAINT name] FOREIGN KEY (col) REFERENCES parent (col) is merged into the child table, as are ADD PRIMARY KEY and ADD UNIQUE. This matters for pg_dump output, which emits every foreign key as an ALTER statement after the tables.

Where can I paste the Mermaid code?

Anywhere Mermaid renders: GitHub and GitLab Markdown, Notion, Obsidian, Confluence (with the Mermaid macro), MkDocs, Docusaurus, VS Code's Markdown preview, and the Mermaid live editor. The crow's-foot symbols (||--o{ and friends) are standard Mermaid erDiagram syntax.

Can I edit the diagram after converting?

Click Open in Studio: the SQL is handed to the ChartCraft studio as the prompt for an ER diagram, where you can rename entities, add tables that are not in the DDL yet, change cardinalities and export SVG or PNG. The first three diagrams are free, no account needed.

Related guides and generators

Need to change the schema? Edit the diagram in the studio.

Open the studio →