SQL DDL Generation
Last updated: March 2026
This reference covers everything between parsing your diagram and emitting SQL — column constraints, relationships, dialect-specific DDL, type mapping, directives, and normalization. Input syntax is documented separately in the Mermaid and PlantUML syntax guides.
Column Markers
Markers control constraints generated on each column:
| Marker | Mermaid | PlantUML | Effect |
|---|---|---|---|
| Primary Key | PK | <<PK>> | Column becomes PRIMARY KEY. If single PK + int type → auto-increment. |
| Foreign Key | FK | <<FK>> | Column participates in ALTER TABLE ... FOREIGN KEY from relationships. |
| Unique | UK | <<UNIQUE>> | Adds UNIQUE constraint on the column. |
| Not Null (marker) | (implicit for PK) | * prefix or <<NOT NULL>> | Adds NOT NULL to the column definition. |
Relationships
Relationships define foreign key constraints between tables. Both Mermaid and PlantUML use similar cardinality notation:
| Notation | Meaning | SQL Result |
|---|---|---|
||--|| | One to one | FK + UNIQUE constraint |
||--o{ | One to many | FK on the "many" side |
}o--|| | Many to one | FK on the "many" side |
}o--o{ | Many to many | Junction table (future) |
-- From this Mermaid relationship:
USER ||--o{ ORDER : places
-- diagram2code generates:
ALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id") REFERENCES "user"("id");fk_{table}_{column}. The FK column is matched by looking for columns named {referenced_table}_id on the "many" side.To add ON DELETE / ON UPDATE cascade actions, use a directive comment on the line immediately above the relationship:
%% ::RELATIONSHIP[onDelete: CASCADE, onUpdate: SET NULL]
USER ||--o{ ORDER : places
-- Generates:
ALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id") REFERENCES "user"("id")
ON DELETE CASCADE
ON UPDATE SET NULL;PostgreSQL postgres
The PostgreSQL generator produces idiomatic PG DDL:
- Auto-increment PK:
SERIAL PRIMARY KEY - Identifier quoting: double-quotes (
"user") - IF NOT EXISTS: supported
- Enums: separate
CREATE TYPE ... AS ENUM(...)statements - Comments:
COMMENT ON COLUMN "table"."col" IS '...' - Boolean: native
BOOLEANtype - UUID: native
UUIDtype
CREATE TABLE "product" (
"id" SERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
"price" NUMERIC,
"active" BOOLEAN DEFAULT true,
"created_at" TIMESTAMPTZ
);
COMMENT ON COLUMN "product"."name" IS 'The display name of the product';MySQL mysql
The MySQL generator produces idiomatic MySQL DDL:
- Auto-increment PK:
INT AUTO_INCREMENT PRIMARY KEY - Identifier quoting: backticks (
`user`) - IF NOT EXISTS: supported
- Enums: inline
ENUM('a', 'b', 'c')in column definition - Comments: inline
COMMENT 'text'on column definition - Boolean: mapped to
TINYINT(1) - UUID: mapped to
CHAR(36) - String: mapped to
VARCHAR(255)(not TEXT)
CREATE TABLE `order` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`status` ENUM('pending', 'shipped', 'delivered') COMMENT 'Order status',
`total` DECIMAL,
`user_id` INT
);
ALTER TABLE `order`
ADD CONSTRAINT `fk_order_user_id`
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`);SQLite sqlite
SQLite uses a simplified type system with type affinity:
- Auto-increment PK:
INTEGER PRIMARY KEY(SQLite auto-increments INTEGER PKs) - Identifier quoting: double-quotes (
"user") - IF NOT EXISTS: supported
- Enums: not supported — falls back to
TEXT - Comments: not supported — comments are omitted
- Boolean: mapped to
INTEGER(0/1) - Date/Time: all mapped to
TEXT(ISO-8601 strings) - Parameterized types: parameters are dropped (e.g.
varchar(255)→TEXT)
CREATE TABLE "product" (
"id" INTEGER PRIMARY KEY,
"name" TEXT,
"price" REAL,
"active" INTEGER,
"created_at" TEXT
);
-- Note: No COMMENT syntax, no ENUM, no UUID.
-- boolean → INTEGER, decimal → REAL, timestamp → TEXT.Oracle 19c+ oracle
The Oracle generator targets Oracle Database 19c and later:
- Auto-increment PK:
NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY - Identifier quoting: double-quotes (
"user") - IF NOT EXISTS: not supported — Oracle lacks this syntax
- Enums: not supported — falls back to
VARCHAR2(255) - Comments:
COMMENT ON COLUMN "table"."col" IS '...' - Boolean: mapped to
NUMBER(1) - UUID: mapped to
RAW(16) - String: mapped to
VARCHAR2(255) - Text/CLOB: mapped to
CLOB - Parameterized types: preserved (e.g.
varchar2(4000)→VARCHAR2(4000))
CREATE TABLE "product" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"name" VARCHAR2(255) NOT NULL,
"price" NUMBER,
"active" NUMBER(1),
"created_at" TIMESTAMP
);
COMMENT ON COLUMN "product"."name" IS 'The display name of the product';CREATE TABLE IF NOT EXISTS. Running the generated DDL twice will produce an error. Use PL/SQL exception handling or check ALL_TABLES before creating.Abstract Type Mapping
Abstract types are the recommended portable types. They map to the best native type for each database:
| Abstract Type | PostgreSQL | MySQL | SQLite | Oracle |
|---|---|---|---|---|
int | INTEGER | INT | INTEGER | NUMBER |
string | TEXT | VARCHAR(255) | TEXT | VARCHAR2(255) |
decimal | NUMERIC | DECIMAL | REAL | NUMBER |
boolean | BOOLEAN | TINYINT(1) | INTEGER | NUMBER(1) |
date | DATE | DATE | TEXT | DATE |
timestamp | TIMESTAMPTZ | DATETIME | TEXT | TIMESTAMP |
uuid | UUID | CHAR(36) | TEXT | RAW(16) |
text | TEXT | TEXT | TEXT | CLOB |
enum | CREATE TYPE | ENUM(...) | TEXT | VARCHAR2(255) |
Native / Pass-through Types
You can also use native database types directly in your diagrams. They are mapped to the closest equivalent in each dialect:
| Native Type | PostgreSQL | MySQL | SQLite | Oracle |
|---|---|---|---|---|
bigint | BIGINT | BIGINT | INTEGER | NUMBER(19) |
smallint | SMALLINT | SMALLINT | INTEGER | NUMBER(5) |
float | DOUBLE PRECISION | FLOAT | REAL | BINARY_DOUBLE |
double | DOUBLE PRECISION | DOUBLE | REAL | BINARY_DOUBLE |
real | REAL | REAL | REAL | BINARY_FLOAT |
varchar | VARCHAR | VARCHAR | TEXT | VARCHAR2 |
varchar2 | VARCHAR | VARCHAR | TEXT | VARCHAR2 |
char | CHAR | CHAR | TEXT | CHAR |
clob | TEXT | LONGTEXT | TEXT | CLOB |
blob | BYTEA | BLOB | BLOB | BLOB |
serial | SERIAL | INT AUTO_INCREMENT | INTEGER | NUMBER |
datetime | TIMESTAMPTZ | DATETIME | TEXT | TIMESTAMP |
bool | BOOLEAN | TINYINT(1) | INTEGER | NUMBER(1) |
Parameterized Types
Types with size or precision parameters like varchar2(4000) or number(10,2) are preserved in dialects that support them:
| Input | PostgreSQL | MySQL | SQLite | Oracle |
|---|---|---|---|---|
varchar2(4000) | VARCHAR(4000) | VARCHAR(4000) | TEXT | VARCHAR2(4000) |
varchar(100) | VARCHAR(100) | VARCHAR(100) | TEXT | VARCHAR2(100) |
number(10,2) | NUMERIC(10,2) | NUMERIC(10,2) | NUMERIC | NUMBER(10,2) |
char(36) | CHAR(36) | CHAR(36) | TEXT | CHAR(36) |
TEXT, and numeric types are reduced to NUMERIC, INTEGER, or REAL based on type affinity.Unknown Types
If you use a type not in the mapping tables (e.g. jsonb, geometry, citext), it is passed through as-is in UPPERCASE:
-- Input: my_custom_type col_name -- Output (all dialects): "col_name" MY_CUSTOM_TYPE
This means you can use database-specific types even if they aren't in the built-in mapping. They will work for your target dialect and pass through unchanged for others.
Comments
Column comments from the diagram are emitted using dialect-specific syntax:
| Dialect | Comment Syntax |
|---|---|
| PostgreSQL | COMMENT ON COLUMN "table"."col" IS 'text'; |
| MySQL | "col" INT COMMENT 'text' (inline) |
| SQLite | Not supported — comments are omitted |
| Oracle | COMMENT ON COLUMN "table"."col" IS 'text'; |
erDiagram
USER {
int id PK "Primary identifier"
string email UK "Login email"
}CREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL UNIQUE
);
COMMENT ON COLUMN "user"."id"
IS 'Primary identifier';
COMMENT ON COLUMN "user"."email"
IS 'Login email';Enums
Enum handling varies significantly by dialect:
| Dialect | Enum Strategy |
|---|---|
| PostgreSQL | Separate CREATE TYPE "table_col_enum" AS ENUM('a','b','c'); before the table. Column type references the created type. |
| MySQL | Inline ENUM('a','b','c') directly in the column definition. |
| SQLite | No enum support — falls back to TEXT. |
| Oracle | No native enum — falls back to VARCHAR2(255). Consider adding a CHECK constraint manually. |
Constraints & Indexes
The following constraints are generated from diagram markers:
- PRIMARY KEY — from
PKmarker. Single integer PKs get auto-increment. Composite PKs usePRIMARY KEY ("col1", "col2"). - FOREIGN KEY — from relationships. Generated as
ALTER TABLE ... ADD CONSTRAINT. - UNIQUE — from
UK/<<UNIQUE>>marker. AddsUNIQUEinline. - NN directive — Non-PK, non-nullable columns get
NOT NULL. - INDEX directive— Indexed columns generate
CREATE INDEX "idx_{table}_{col}" ON "table"("col").
Default Values
Default values are set via directive comments and emitted with automatic quoting: numeric values are unquoted, string values are single-quoted.
-- Numeric default: DEFAULT 0 -- String default: DEFAULT 'active' -- Boolean default: DEFAULT true (postgres) / DEFAULT 1 (mysql/sqlite/oracle)
See the Directives section for full ::DEFAULT(value) syntax and examples.
Directives
Directives are structured comment annotations that extend your ER diagram with metadata that standard Mermaid and PlantUML syntax cannot express — things like NOT NULL on a Mermaid column, default column values, and ON DELETE / ON UPDATE cascade actions on foreign keys.
Syntax
| Format | Mermaid | PlantUML |
|---|---|---|
| Column directive | %% ::DIRECTIVE | ' ::DIRECTIVE |
| Relationship directive | %% ::DIRECTIVE | ' ::DIRECTIVE |
| Alternate comment style | — | // ::DIRECTIVE |
::) are silently ignored and remain fully backward compatible.::NN — Not Null
Marks the next column as NOT NULL. This is especially useful in Mermaid, where there is no built-in NOT NULL marker outside of the PK constraint. (In PlantUML, * prefix and <<NOT NULL>> are alternatives.)
::NNerDiagram
USER {
int id PK
%% ::NN
string email
string bio
}CREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL,
"bio" TEXT
);::NN@startuml
entity User {
* id : int <<PK>>
' ::NN
email : string
bio : string
}
@endumlCREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL,
"bio" TEXT
);::DEFAULT(value) — Default Value
Sets a DEFAULT on the next column. The value is type-aware: numeric literals are emitted unquoted; everything else is single-quoted.
::DEFAULTerDiagram
ORDER {
int id PK
%% ::DEFAULT(pending)
string status
%% ::DEFAULT(0)
decimal total
}CREATE TABLE "order" (
"id" SERIAL PRIMARY KEY,
"status" TEXT DEFAULT 'pending',
"total" NUMERIC DEFAULT 0
);::DEFAULT@startuml
entity Order {
* id : int <<PK>>
' ::DEFAULT(pending)
status : string
// ::DEFAULT(0)
total : decimal
}
@endumlCREATE TABLE `order` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`status` VARCHAR(255) DEFAULT 'pending',
`total` DECIMAL DEFAULT 0
);::NN and ::DEFAULT on adjacent lines before a column — both will be applied:%% ::NN %% ::DEFAULT(active) string statusThis generates:
"status" TEXT NOT NULL DEFAULT 'active'::RELATIONSHIP[...] — Cascade Actions
Adds ON DELETE and/or ON UPDATE referential actions to the foreign key generated by the next relationship line.
| Key | Supported Values |
|---|---|
onDelete | CASCADE · SET NULL · RESTRICT · NO ACTION · SET DEFAULT |
onUpdate | CASCADE · SET NULL · RESTRICT · NO ACTION · SET DEFAULT |
erDiagram
USER {
int id PK
string name
}
ORDER {
int id PK
int user_id FK
decimal total
}
%% ::RELATIONSHIP[onDelete: CASCADE]
USER ||--o{ ORDER : placesALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id")
REFERENCES "user"("id")
ON DELETE CASCADE;@startuml
entity User {
* id : int <<PK>>
name : string
}
entity Order {
* id : int <<PK>>
user_id : int <<FK>>
total : decimal
}
' ::RELATIONSHIP[onDelete: CASCADE, onUpdate: SET NULL]
User ||--o{ Order : places
@endumlALTER TABLE `order`
ADD CONSTRAINT `fk_order_user_id`
FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`)
ON DELETE CASCADE
ON UPDATE SET NULL;Schema Normalization
Before generating SQL, the schema goes through normalization:
- Synthetic PK: Tables without any
PKcolumn automatically get anid int PKcolumn added. A warning is emitted. - Duplicate columns: If a table has duplicate column names, only the first is kept. A warning is emitted.
- Warning severity: Normalization issues produce warnings (not errors), so SQL is still generated.
stderr) or API response (warnings array) to catch schema issues early.Full Example: Directives in Action
This example combines all three P0 directives — ::NN, ::DEFAULT, and ::RELATIONSHIP — in a realistic blog schema with users, posts, and comments.
erDiagram
USER {
int id PK
%% ::NN
string username
%% ::NN
string email
%% ::DEFAULT(active)
string status
%% ::DEFAULT(0)
int login_count
timestamp created_at
}
POST {
int id PK
int user_id FK
%% ::NN
string title
%% ::DEFAULT(draft)
string status
boolean published
timestamp created_at
}
COMMENT {
int id PK
int post_id FK
int user_id FK
%% ::NN
text body
timestamp created_at
}
%% ::RELATIONSHIP[onDelete: CASCADE]
USER ||--o{ POST : writes
%% ::RELATIONSHIP[onDelete: CASCADE]
POST ||--o{ COMMENT : has
%% ::RELATIONSHIP[onDelete: SET NULL]
USER ||--o{ COMMENT : authorsCREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"status" TEXT DEFAULT 'active',
"login_count" INTEGER DEFAULT 0,
"created_at" TIMESTAMP
);
CREATE TABLE "post" (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER,
"title" TEXT NOT NULL,
"status" TEXT DEFAULT 'draft',
"published" BOOLEAN,
"created_at" TIMESTAMP
);
CREATE TABLE "comment" (
"id" SERIAL PRIMARY KEY,
"post_id" INTEGER,
"user_id" INTEGER,
"body" TEXT NOT NULL,
"created_at" TIMESTAMP
);
ALTER TABLE "post"
ADD CONSTRAINT "fk_post_user_id"
FOREIGN KEY ("user_id")
REFERENCES "user"("id")
ON DELETE CASCADE;
ALTER TABLE "comment"
ADD CONSTRAINT "fk_comment_post_id"
FOREIGN KEY ("post_id")
REFERENCES "post"("id")
ON DELETE CASCADE;
ALTER TABLE "comment"
ADD CONSTRAINT "fk_comment_user_id"
FOREIGN KEY ("user_id")
REFERENCES "user"("id")
ON DELETE SET NULL;The same diagram with PlantUML syntax using ' directive comments:
@startuml
entity User {
* id : int <<PK>>
' ::NN
username : string
' ::NN
email : string
' ::DEFAULT(active)
status : string
' ::DEFAULT(0)
login_count : int
created_at : timestamp
}
entity Post {
* id : int <<PK>>
user_id : int <<FK>>
' ::NN
title : string
' ::DEFAULT(draft)
status : string
published : boolean
created_at : timestamp
}
entity Comment {
* id : int <<PK>>
post_id : int <<FK>>
user_id : int <<FK>>
' ::NN
body : text
created_at : timestamp
}
' ::RELATIONSHIP[onDelete: CASCADE]
User ||--o{ Post : writes
' ::RELATIONSHIP[onDelete: CASCADE]
Post ||--o{ Comment : has
' ::RELATIONSHIP[onDelete: SET NULL]
User ||--o{ Comment : authors
@endumlCREATE TABLE `user` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`status` VARCHAR(255) DEFAULT 'active',
`login_count` INT DEFAULT 0,
`created_at` DATETIME
);
CREATE TABLE `post` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT,
`title` VARCHAR(255) NOT NULL,
`status` VARCHAR(255) DEFAULT 'draft',
`published` TINYINT(1),
`created_at` DATETIME
);
CREATE TABLE `comment` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`post_id` INT,
`user_id` INT,
`body` TEXT NOT NULL,
`created_at` DATETIME
);
ALTER TABLE `post`
ADD CONSTRAINT `fk_post_user_id`
FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`)
ON DELETE CASCADE;
ALTER TABLE `comment`
ADD CONSTRAINT `fk_comment_post_id`
FOREIGN KEY (`post_id`)
REFERENCES `post`(`id`)
ON DELETE CASCADE;
ALTER TABLE `comment`
ADD CONSTRAINT `fk_comment_user_id`
FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`)
ON DELETE SET NULL;%% ::NN/' ::NN—username,email,title, andbodyare required (NOT NULL)::DEFAULT(active)/::DEFAULT(draft)— string defaults are single-quoted in the output::DEFAULT(0)— numeric default is emitted unquoted::RELATIONSHIP[onDelete: CASCADE]— deleting a user cascades to their posts; deleting a post cascades to its comments::RELATIONSHIP[onDelete: SET NULL]— deleting a user setsuser_idtoNULLon orphaned comments (preserving comment history)
Full Example: E-Commerce Schema
erDiagram
CUSTOMER {
int id PK
string name
string email UK
timestamp created_at
}
PRODUCT {
int id PK
string title
decimal price
text description
boolean in_stock
}
ORDER {
int id PK
int customer_id FK
timestamp ordered_at
decimal total
}
ORDER_ITEM {
int id PK
int order_id FK
int product_id FK
int quantity
decimal unit_price
}
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT ||--o{ ORDER_ITEM : "listed in"CREATE TABLE `customer` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255),
`email` VARCHAR(255) NOT NULL UNIQUE,
`created_at` DATETIME
);
CREATE TABLE `product` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255),
`price` DECIMAL,
`description` TEXT,
`in_stock` TINYINT(1)
);
CREATE TABLE `order` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` INT,
`ordered_at` DATETIME,
`total` DECIMAL
);
CREATE TABLE `order_item` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`order_id` INT,
`product_id` INT,
`quantity` INT,
`unit_price` DECIMAL
);
ALTER TABLE `order`
ADD CONSTRAINT `fk_order_customer_id`
FOREIGN KEY (`customer_id`)
REFERENCES `customer`(`id`);
ALTER TABLE `order_item`
ADD CONSTRAINT `fk_order_item_order_id`
FOREIGN KEY (`order_id`)
REFERENCES `order`(`id`);
ALTER TABLE `order_item`
ADD CONSTRAINT `fk_order_item_product_id`
FOREIGN KEY (`product_id`)
REFERENCES `product`(`id`);Full Example: Blog Platform
@startuml
entity Author {
* id : int <<PK>>
name : string
email : string <<UNIQUE>>
bio : text
}
entity Post {
* id : int <<PK>>
author_id : int <<FK>>
title : string
body : text
published : boolean
created_at : timestamp
}
entity Tag {
* id : int <<PK>>
name : string <<UNIQUE>>
}
Author ||--o{ Post : writes
@endumlCREATE TABLE "author" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
"name" VARCHAR2(255),
"email" VARCHAR2(255) NOT NULL UNIQUE,
"bio" CLOB
);
CREATE TABLE "post" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
"author_id" NUMBER,
"title" VARCHAR2(255),
"body" CLOB,
"published" NUMBER(1),
"created_at" TIMESTAMP
);
CREATE TABLE "tag" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
"name" VARCHAR2(255) NOT NULL UNIQUE
);
ALTER TABLE "post"
ADD CONSTRAINT "fk_post_author_id"
FOREIGN KEY ("author_id")
REFERENCES "author"("id");Same Diagram → All 4 Dialects
Here's how the same simple diagram renders across all supported databases:
erDiagram
USER {
int id PK
string name
string email
}
ORDER {
int id PK
int user_id FK
decimal total
}
USER ||--o{ ORDER : placesCREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"name" TEXT,
"email" TEXT
);
CREATE TABLE "order" (
"id" SERIAL PRIMARY KEY,
"user_id" INTEGER,
"total" NUMERIC
);
ALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id")
REFERENCES "user"("id");CREATE TABLE `user` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255),
`email` VARCHAR(255)
);
CREATE TABLE `order` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT,
`total` DECIMAL
);
ALTER TABLE `order`
ADD CONSTRAINT `fk_order_user_id`
FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`);CREATE TABLE "user" (
"id" INTEGER PRIMARY KEY,
"name" TEXT,
"email" TEXT
);
CREATE TABLE "order" (
"id" INTEGER PRIMARY KEY,
"user_id" INTEGER,
"total" REAL
);
ALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id")
REFERENCES "user"("id");CREATE TABLE "user" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"name" VARCHAR2(255),
"email" VARCHAR2(255)
);
CREATE TABLE "order" (
"id" NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
"user_id" NUMBER,
"total" NUMBER
);
ALTER TABLE "order"
ADD CONSTRAINT "fk_order_user_id"
FOREIGN KEY ("user_id") REFERENCES "user"("id");