Appearance
Relationships
Relationships (refs) define foreign key connections between tables. They are rendered as lines with arrows on the ERD canvas, and produce FOREIGN KEY constraints or ALTER TABLE statements in the generated SQL.
Standalone Syntax
Ref: table1.column > table2.columnA standalone Ref is defined outside of any table block. It connects a column in one table to a column in another.
Relationship Types
| Symbol | Type | Meaning |
|---|---|---|
> | Many-to-one | Many records in the left table reference one record in the right table |
< | One-to-many | One record in the left table is referenced by many records in the right table |
- | One-to-one | Exactly one record on each side |
<> | Many-to-many | Multiple records on both sides |
Many-to-One (>)
The most common relationship type. Use this when a column holds a foreign key pointing to another table's primary key.
// Each post belongs to one user, but a user can have many posts
Ref: posts.user_id > users.idOn the canvas, the arrow points from posts.user_id toward users.id, with a "many" indicator on the posts side and a "one" indicator on the users side.
One-to-Many (<)
The reverse of many-to-one. Functionally equivalent — it just reads in the opposite direction:
// One user has many posts
Ref: users.id < posts.user_idThis produces the same foreign key as Ref: posts.user_id > users.id. Choose whichever direction reads more naturally for your use case.
One-to-One (-)
Use this when each record in one table corresponds to exactly one record in another:
// Each user has exactly one profile
Ref: users.id - profiles.user_idA one-to-one relationship is typically enforced by adding a UNIQUE constraint on the foreign key column (profiles.user_id).
Many-to-Many (<>)
Represents an association where records on both sides can relate to multiple records on the other side:
// Students can enroll in many courses; courses have many students
Ref: students.id <> courses.idTIP
In practice, most databases implement many-to-many relationships through a junction (join) table. You may prefer to model this explicitly:
Table enrollments {
student_id int [ref: > students.id]
course_id int [ref: > courses.id]
}Named Relationships
Give a ref a name for clarity in complex schemas:
Ref user_posts: posts.user_id > users.id
Ref post_comments: comments.post_id > posts.idThe name appears as a label and makes it easier to identify specific relationships in large diagrams.
Inline Relationships
Instead of standalone Ref statements, you can define relationships directly inside a table's column settings using the ref: setting:
Table posts {
id int [pk, increment]
user_id int [not null, ref: > users.id]
category_id int [ref: > categories.id]
}This is equivalent to writing separate Ref: posts.user_id > users.id and Ref: posts.category_id > categories.id statements.
When to Use Inline vs Standalone
Inline refs are convenient when:
- The relationship is simple (single column)
- You want to see the foreign key right next to the column definition
- You prefer keeping related information together
Standalone refs are better when:
- You need named relationships
- You want to add delete/update actions
- You have composite foreign keys
- You prefer a separate section listing all relationships for easy scanning
Self-Referential Relationships
A table can reference itself. This is common for hierarchical data:
Table employees {
id int [pk, increment]
name varchar(100) [not null]
manager_id int [ref: > employees.id]
}
// Equivalent standalone syntax:
Ref: employees.manager_id > employees.idAnother common example is a categories tree:
Table categories {
id int [pk, increment]
name varchar(100) [not null]
parent_id int
}
Ref: categories.parent_id > categories.idOn the canvas, self-referential relationships render as a line that loops back to the same table.
Composite Foreign Keys
When a foreign key spans multiple columns, use parentheses to group them:
Table order_items {
order_id int [not null]
product_id int [not null]
warehouse_id int [not null]
quantity int [not null]
}
Table inventory {
product_id int [not null]
warehouse_id int [not null]
stock int [not null]
}
// Composite foreign key: both columns must match together
Ref: order_items.(product_id, warehouse_id) > inventory.(product_id, warehouse_id)The column order matters — the first column on the left maps to the first column on the right, and so on.
Delete / Update Actions
Control what happens to referencing rows when the referenced row is deleted or updated:
Ref: posts.user_id > users.id [delete: cascade, update: no action]
Ref: comments.post_id > posts.id [delete: cascade]
Ref: orders.customer_id > customers.id [delete: set null]Supported Actions
| Action | On Delete | On Update |
|---|---|---|
no action | Raise an error if referencing rows exist (checked at end of transaction). This is the default behavior. | Raise an error if the referenced key changes. |
restrict | Raise an error immediately if referencing rows exist. Similar to no action but checked immediately. | Raise an error immediately if the referenced key changes. |
cascade | Delete all rows that reference the deleted row. | Update the foreign key in all referencing rows to match the new value. |
set null | Set the foreign key column(s) to NULL in all referencing rows. The column must be nullable. | Set the foreign key column(s) to NULL. |
set default | Set the foreign key column(s) to their default value. The column must have a default defined. | Set the foreign key column(s) to their default value. |
WARNING
set null requires that the foreign key column allows NULL values. If the column is defined with not null, the database will raise an error at runtime.
TIP
cascade on delete is useful for parent-child relationships (e.g., deleting an order also deletes its line items). Be careful using it on user-facing tables — accidentally deleting a user could cascade and remove all their data.
Canvas Rendering
Relationships are displayed as lines connecting the related columns on the ERD canvas:
- Many-to-one (
>) — Line with a "crow's foot" (many) on the source side and a single line (one) on the target side - One-to-one (
-) — Single line on both sides - Many-to-many (
<>) — Crow's foot on both sides
You can drag and rearrange tables on the canvas to keep relationship lines clean and readable. The lines automatically re-route as you move tables around.
Complete Example
Table users {
id int [pk, increment]
email varchar(255) [not null, unique]
}
Table posts {
id int [pk, increment]
user_id int [not null, ref: > users.id]
title varchar(200) [not null]
}
Table comments {
id int [pk, increment]
post_id int [not null]
user_id int [not null]
parent_id int
body text [not null]
}
// Standalone refs with delete actions
Ref: comments.post_id > posts.id [delete: cascade]
Ref: comments.user_id > users.id [delete: cascade]
// Self-referential: threaded comments
Ref: comments.parent_id > comments.id [delete: cascade]This example combines inline and standalone refs, includes delete actions, and demonstrates a self-referential relationship for threaded comments.