Home Database ER Diagrams Explained: Entities, Relationships and Keys for Beginners

ER Diagrams Explained: Entities, Relationships and Keys for Beginners

In Plain English 🔥
Think of an ER diagram like an architect's blueprint before a house is built — nobody picks up a hammer until the plan is drawn. An ER diagram is that blueprint for a database: it maps out what things you need to store (like Students and Courses) and how those things connect to each other, before you write a single line of SQL. Just like a blueprint uses standard symbols so any builder can read it, an ER diagram uses standard shapes so any developer or database admin can understand the plan instantly.
⚡ Quick Answer
Think of an ER diagram like an architect's blueprint before a house is built — nobody picks up a hammer until the plan is drawn. An ER diagram is that blueprint for a database: it maps out what things you need to store (like Students and Courses) and how those things connect to each other, before you write a single line of SQL. Just like a blueprint uses standard symbols so any builder can read it, an ER diagram uses standard shapes so any developer or database admin can understand the plan instantly.

Every app you've ever used — Instagram, Spotify, your school's grading portal — stores data in a database. But before that database was built, someone had to answer a deceptively hard question: what exactly are we storing, and how does it all fit together? Get that wrong at the start and you end up with a tangled mess of tables that are nearly impossible to untangle later. ER diagrams exist precisely to force that thinking before any real work begins.

The problem ER diagrams solve is miscommunication. A business analyst says 'a customer can have multiple orders.' A developer hears something slightly different and builds the database wrong. An ER diagram turns that fuzzy conversation into a precise, visual contract that every stakeholder — technical or not — can read and agree on. It's a universal language for data structure.

By the end of this article you'll be able to read any ER diagram you come across, draw one from a plain-English description of a system, spot common design mistakes before they become expensive bugs, and walk into a database interview with genuine confidence. We'll build everything from scratch using a relatable example — a simple online bookstore — and layer on complexity one piece at a time.

What an ER Diagram Actually Is (and Why It Exists)

ER stands for Entity-Relationship. The diagram was invented by computer scientist Peter Chen in 1976, and the core idea hasn't changed since: draw pictures of your data before you build anything.

There are three foundational building blocks you need to know right now, and everything else is just detail on top of them.

An Entity is a real-world thing you want to store information about. In a bookstore, that's things like Book, Customer, and Order. Think of each entity as a category — not a specific book, but the concept of a book.

An Attribute is a piece of information that describes an entity. A Book entity has attributes like Title, ISBN, and Price. A Customer has a Name, Email, and Address. Attributes are the columns your future database table will have.

A Relationship is the connection between two entities. A Customer places an Order. An Order contains Books. These verbs connecting your nouns are the relationships.

That's the whole model. Everything else — notation styles, cardinality symbols, keys — is just a way to be more precise about these three ideas. Start with entities, hang attributes on them, then draw lines between the entities to show how they relate.

bookstore_er_sketch.txt · PLAINTEXT
123456789101112131415161718192021
=== BOOKSTORE ER DIAGRAM (Text Representation) ===

[ CUSTOMER ]                    [ ORDER ]                    [ BOOK ]
-----------                    ---------                    --------
* CustomerID (PK)              * OrderID (PK)               * ISBN (PK)
  FirstName                    * OrderDate                    Title
  LastName                     * TotalAmount                  Author
  Email                        * CustomerID (FK)              Price
  Phone                                                        StockCount

  CUSTOMER ----places----> ORDER ----contains----> BOOK
  (one customer places      (one order contains
   many orders)              many books; one book
                             can appear in many orders)

LEGEND:
  [ ]  = Entity (becomes a table)
  *    = Part of Primary Key
  (PK) = Primary Key — uniquely identifies each row
  (FK) = Foreign Key — links to another table
  ---- = Relationship line
▶ Output
This is a design artifact, not executable code.
When implemented in SQL, this diagram produces three tables:
- customer (5 columns)
- order (4 columns)
- book (5 columns)
Plus one junction table: order_book (to handle the many-to-many relationship between orders and books)
🔥
Why Draw First, Code Second:Studies of failed software projects consistently show that fixing a database design mistake after data is live costs 10-100x more than catching it on a whiteboard diagram. An ER diagram is cheap. Migrating a production database with millions of rows is not.

Entities, Attributes and Keys — The Building Blocks in Detail

Let's go deeper on each building block using our bookstore example so every concept has a concrete home to live in.

Entities become tables in your database. Strong entities can exist on their own — a Book exists whether or not it's been ordered. Weak entities depend on another entity to exist — an OrderItem only makes sense if there's an Order it belongs to. In diagrams, strong entities are drawn as rectangles; weak entities use a double rectangle.

Attributes come in flavors. A simple attribute holds one value (a customer's LastName). A multi-valued attribute can hold several (a customer might have two Phone numbers). A derived attribute is calculated from others (Age can be derived from DateOfBirth — you don't store it, you compute it on the fly). Composite attributes are made of smaller parts (FullName is made of FirstName + LastName).

Keys are the most important concept to nail down. A Primary Key (PK) is the attribute — or combination of attributes — that uniquely identifies one row in a table. No two rows can share the same Primary Key value, ever. CustomerID is a perfect primary key because no two customers share one. Email could work too, but people change emails, so auto-generated numeric IDs are safer. A Foreign Key (FK) is how you create the link between two tables — it's a column in one table that stores the Primary Key of a related row in another table.

bookstore_create_tables.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
-- ============================================================
-- BOOKSTORE DATABASE — translating our ER diagram into SQL
-- These CREATE TABLE statements are the direct output of
-- the design decisions made in the ER diagram above.
-- ============================================================

-- ENTITY: Customer
-- CustomerID is an auto-incrementing integer — the database
-- assigns it automatically so we never get duplicates.
CREATE TABLE customer (
    customer_id   INT          PRIMARY KEY AUTO_INCREMENT,  -- surrogate PK
    first_name    VARCHAR(50)  NOT NULL,
    last_name     VARCHAR(50)  NOT NULL,
    email         VARCHAR(100) NOT NULL UNIQUE,             -- natural candidate key
    phone         VARCHAR(20)                               -- nullable: optional field
);

-- ENTITY: Book
-- ISBN is a real-world unique code for every book — a natural PK.
CREATE TABLE book (
    isbn          CHAR(13)     PRIMARY KEY,   -- ISBNs are always exactly 13 digits
    title         VARCHAR(200) NOT NULL,
    author        VARCHAR(100) NOT NULL,
    price         DECIMAL(6,2) NOT NULL,      -- 6 digits total, 2 after decimal point
    stock_count   INT          NOT NULL DEFAULT 0
);

-- ENTITY: Order
-- customer_id here is a FOREIGN KEY — it references customer(customer_id)
-- This is how the ER relationship "Customer places Order" becomes real in SQL.
CREATE TABLE `order` (
    order_id      INT          PRIMARY KEY AUTO_INCREMENT,
    order_date    DATE         NOT NULL,
    total_amount  DECIMAL(8,2) NOT NULL,
    customer_id   INT          NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customer(customer_id)
    -- If you try to insert an order with a customer_id that doesn't exist
    -- in the customer table, the database will REJECT it. This enforces
    -- referential integrity — no orphan orders allowed.
);

-- JUNCTION TABLE: order_book
-- This resolves the many-to-many relationship between Order and Book.
-- One order can contain many books. One book can appear in many orders.
-- A junction table is the ONLY correct way to model many-to-many in SQL.
CREATE TABLE order_book (
    order_id      INT          NOT NULL,
    isbn          CHAR(13)     NOT NULL,
    quantity      INT          NOT NULL DEFAULT 1,
    unit_price    DECIMAL(6,2) NOT NULL,   -- price at time of purchase (may change later)
    PRIMARY KEY (order_id, isbn),          -- composite PK: combination must be unique
    FOREIGN KEY (order_id) REFERENCES `order`(order_id),
    FOREIGN KEY (isbn)     REFERENCES book(isbn)
);
▶ Output
Query OK, 0 rows affected (table: customer)
Query OK, 0 rows affected (table: book)
Query OK, 0 rows affected (table: order)
Query OK, 0 rows affected (table: order_book)

Database now contains 4 tables that perfectly reflect the ER diagram.
Inserting an order for a non-existent customer will produce:
ERROR 1452: Cannot add or update a child row: a foreign key constraint fails
⚠️
Watch Out: Don't Use Email as a Primary KeyIt seems logical — emails are unique. But people change their email addresses. If email is your PK, changing one customer's email means updating it in every related table (orders, reviews, sessions). Use a surrogate auto-increment ID as your PK and store email as a UNIQUE column. The ER diagram is the right place to catch this mistake before it's baked into your schema.

Relationships and Cardinality — How Entities Talk to Each Other

Drawing a line between two entities is only half the job. You also need to say how many of one thing relates to how many of the other. That's cardinality, and it's the most important and most misunderstood part of ER diagrams.

There are three cardinality types you'll encounter in every system you ever design.

One-to-One (1:1): One row in Table A links to exactly one row in Table B, and vice versa. Example: one Customer has one ShippingProfile. These are rare and often a sign you should just merge the tables. Use it when you want to separate sensitive data (like storing payment details in a separate, more secured table).

One-to-Many (1:N): One row in Table A links to many rows in Table B, but each row in B links to only one row in A. This is the most common relationship in any database. One Customer places many Orders. One Author writes many Books. In SQL, you implement this by putting the FK on the 'many' side.

Many-to-Many (M:N): One row in Table A can link to many rows in Table B, and one row in Table B can link to many rows in Table A. Students enrol in many Courses; each Course has many Students. You cannot implement this directly in SQL — you always need a junction table in between. Your ER diagram must show this before you try to build it.

The notation you'll see most in job descriptions is Crow's Foot notation — a 'crow's foot' symbol (a trident shape) on the 'many' end of a relationship line, and a single vertical bar on the 'one' end.

cardinality_reference.txt · PLAINTEXT
1234567891011121314151617181920212223242526272829303132333435363738394041424344
=== CARDINALITY CHEAT SHEET (Crow's Foot Notation) ===

── ONE-TO-ONE ─────────────────────────────────────────

  CUSTOMER ──|────|── SHIPPING_PROFILE
             one  one

  "Each customer has exactly one shipping profile."
  "Each shipping profile belongs to exactly one customer."
  SQL: Put the FK in either table (usually the 'weaker' one).

── ONE-TO-MANY ────────────────────────────────────────

  CUSTOMER ──|────<── ORDER
             one    many

  "One customer places many orders."
  "Each order belongs to exactly one customer."
  SQL: FK (customer_id) goes in the ORDER table — the 'many' side.
       Never put it in the CUSTOMER table.

── MANY-TO-MANY ───────────────────────────────────────

  ORDER ──>────<── BOOK
        many  many

  "One order contains many books."
  "One book appears in many orders."
  SQL: You CANNOT model this directly.
       Create a junction table: ORDER_BOOK(order_id FK, isbn FK)
       This splits the M:N into two 1:N relationships:
         ORDER ──|────<── ORDER_BOOK ──>────|── BOOK

── CROW'S FOOT SYMBOLS ────────────────────────────────

  |    = exactly one      (mandatory one)
  O    = zero or one      (optional one)
  <    = many             (mandatory many)
  O<   = zero or many     (optional many)

  Combined examples:
  |──|   = exactly one on both sides
  |──<   = one on left, many on right
  O──O<  = optional one to optional many
▶ Output
This is a reference diagram — no executable output.
Use this as a quick lookup when you're reading or drawing ER diagrams.
The most common pattern you'll see in real databases is: |──< (one-to-many)
⚠️
Pro Tip: Read Relationships in Both DirectionsEvery relationship line tells two stories. 'Customer places Orders' reads left to right. 'Each Order belongs to one Customer' reads right to left. Always read both directions aloud when reviewing a diagram — hidden design errors become obvious when you verbalize both sides. If either direction sounds wrong, your cardinality is wrong.

Drawing Your First Complete ER Diagram — A Step-by-Step Walkthrough

Knowing the pieces is one thing. Knowing how to go from a plain-English description to a finished diagram is the skill that actually matters. Here's the repeatable process senior developers use.

Step 1 — Identify your entities. Read the requirements and underline every noun. 'Students enrol in Courses taught by Instructors.' The nouns — Student, Course, Instructor — are your candidate entities.

Step 2 — Identify attributes for each entity. Ask: what do I need to know about a Student? Name, StudentID, Email, DateOfBirth. Repeat for every entity. Circle anything that could uniquely identify one row — that's your Primary Key candidate.

Step 3 — Identify relationships. Read the verbs. 'Students enrol in Courses.' 'Instructors teach Courses.' Each verb is a relationship. Write them down as triples: (Student, enrols in, Course).

Step 4 — Determine cardinality. For each relationship, ask both directions: 'Can one Student enrol in many Courses?' Yes. 'Can one Course have many Students?' Yes. That's Many-to-Many — you need a junction table called Enrolment.

Step 5 — Add foreign keys. Now that you know the cardinality, add FKs on the 'many' sides. For M:N, add both FKs into the junction table.

Step 6 — Review. Read every relationship both directions aloud. Check every entity has a PK. Confirm no M:N relationship is missing a junction table. Done.

university_schema_from_er.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
-- ============================================================
-- UNIVERSITY ENROLLMENT SYSTEM
-- Built by following the 6-step ER diagram process above.
-- Requirements: Students enrol in Courses taught by Instructors.
-- ============================================================

-- Step 1-2: Entity INSTRUCTOR with attributes
CREATE TABLE instructor (
    instructor_id   INT          PRIMARY KEY AUTO_INCREMENT,
    full_name       VARCHAR(100) NOT NULL,
    email           VARCHAR(100) NOT NULL UNIQUE,
    department      VARCHAR(50)  NOT NULL
);

-- Step 1-2: Entity COURSE with attributes
-- Step 5: instructor_id FK links to the instructor who teaches this course
-- Cardinality: One instructor teaches many courses (1:N)
--              FK goes on the 'many' side — in the COURSE table
CREATE TABLE course (
    course_id       CHAR(8)      PRIMARY KEY,   -- e.g. 'CS-10234'
    course_name     VARCHAR(150) NOT NULL,
    credits         TINYINT      NOT NULL CHECK (credits BETWEEN 1 AND 6),
    instructor_id   INT          NOT NULL,
    FOREIGN KEY (instructor_id) REFERENCES instructor(instructor_id)
);

-- Step 1-2: Entity STUDENT with attributes
CREATE TABLE student (
    student_id      INT          PRIMARY KEY AUTO_INCREMENT,
    first_name      VARCHAR(50)  NOT NULL,
    last_name       VARCHAR(50)  NOT NULL,
    email           VARCHAR(100) NOT NULL UNIQUE,
    enrollment_year YEAR         NOT NULL
);

-- Step 4-5: JUNCTION TABLE for the M:N relationship between Student and Course
-- 'Students enrol in Courses' — one student takes many courses,
-- one course has many students. We CANNOT link these directly.
-- The ENROLMENT table breaks it into two 1:N relationships.
CREATE TABLE enrolment (
    student_id      INT          NOT NULL,
    course_id       CHAR(8)      NOT NULL,
    enrolment_date  DATE         NOT NULL,
    grade           CHAR(2),                   -- NULL until grade is submitted
    PRIMARY KEY (student_id, course_id),       -- composite PK prevents duplicate enrolment
    FOREIGN KEY (student_id) REFERENCES student(student_id),
    FOREIGN KEY (course_id)  REFERENCES course(course_id)
);

-- ── SAMPLE DATA to verify the design works ──────────────────

INSERT INTO instructor VALUES (1, 'Dr. Priya Mehta', 'priya@uni.edu', 'Computer Science');

INSERT INTO course VALUES ('CS-10234', 'Database Systems', 3, 1);
INSERT INTO course VALUES ('CS-10301', 'Algorithms',       3, 1);

INSERT INTO student VALUES (1, 'James', 'Okafor', 'james@uni.edu', 2023);
INSERT INTO student VALUES (2, 'Sofia', 'Reyes',  'sofia@uni.edu', 2023);

-- James enrols in both courses; Sofia enrols in one
INSERT INTO enrolment VALUES (1, 'CS-10234', '2024-09-01', NULL);
INSERT INTO enrolment VALUES (1, 'CS-10301', '2024-09-01', NULL);
INSERT INTO enrolment VALUES (2, 'CS-10234', '2024-09-01', NULL);

-- Query that PROVES the ER design is correct:
-- Show every student and every course they are enrolled in
SELECT
    CONCAT(s.first_name, ' ', s.last_name) AS student_name,
    c.course_name,
    i.full_name                            AS taught_by
FROM enrolment e
JOIN student    s ON e.student_id = s.student_id
JOIN course     c ON e.course_id  = c.course_id
JOIN instructor i ON c.instructor_id = i.instructor_id
ORDER BY student_name, c.course_name;
▶ Output
student_name | course_name | taught_by
----------------|-------------------|------------------
James Okafor | Algorithms | Dr. Priya Mehta
James Okafor | Database Systems | Dr. Priya Mehta
Sofia Reyes | Database Systems | Dr. Priya Mehta

3 rows returned.

The design works: James appears twice (enrolled in 2 courses),
Sofia appears once. No duplicate data in any table.
🔥
Interview Gold: The Junction Table QuestionInterviewers love to ask 'how do you implement a many-to-many relationship in a relational database?' The answer is always a junction table (also called a bridge table, associative table, or linking table). Bonus points if you mention that the junction table often grows its own attributes — like the 'grade' column in our enrolment table — making it a full entity in its own right.
AspectOne-to-Many (1:N)Many-to-Many (M:N)
Real-world exampleOne Customer → Many OrdersStudents ↔ Courses
Where does the FK go?In the 'many' side table (Order table)In a separate junction table — never in the main entities
Number of tables needed2 tables3 tables minimum (both entities + junction)
Composite Primary Key needed?No — each table has its own single PKYes — junction table PK combines both FKs
Most common mistakePutting FK on the wrong side (in the 'one' table)Trying to store the relationship in a comma-separated column
How to spot it in requirementsVerb is singular on one side: 'a customer places orders'Both sides are plural: 'students enrol in courses, courses have students'
ER diagram symbol (Crow's Foot)Single bar ── Crow's foot: |──<Crow's foot on both ends: >──<

🎯 Key Takeaways

  • An ER diagram is a blueprint, not code — draw it before you write a single line of SQL, because fixing a design on paper costs nothing and fixing it in production costs everything.
  • The three core building blocks are Entity (thing), Attribute (fact about a thing), and Relationship (connection between things) — every ER concept is just a refinement of these three.
  • Cardinality is the critical detail: One-to-Many is implemented with a FK on the 'many' table; Many-to-Many always requires a junction table — there are no exceptions to this rule in relational databases.
  • A Foreign Key is the physical implementation of a relationship line in your ER diagram — when you draw that line, you're deciding where the FK column will live.

⚠ Common Mistakes to Avoid

  • Mistake 1: Trying to model a Many-to-Many relationship with a comma-separated column — e.g. storing '101,205,310' in a course_ids column in the student table. This looks clever but destroys your ability to query, index, or enforce foreign keys. Symptom: you write queries with string-splitting functions and they're slow and fragile. Fix: always create a junction table and store one relationship per row.
  • Mistake 2: Putting the Foreign Key on the wrong side of a One-to-Many relationship — e.g. adding a column order_ids to the customer table instead of adding customer_id to the order table. Symptom: you end up with arrays or comma-separated IDs again, or you can't add a second order without modifying the customer row. Fix: the FK always lives in the 'many' table. Ask yourself 'which side can have multiple values?' — that side gets the FK.
  • Mistake 3: Creating entities for everything, including things that should just be attributes — e.g. making 'Color' its own entity with a ColorID just because it sounds like a noun. Symptom: your diagram has 40 entities for a simple app and joins are everywhere. Fix: if a concept has no attributes of its own and never connects to more than one entity, it's an attribute, not an entity. Color = attribute of Product. Supplier = entity (it has a name, address, phone).

Interview Questions on This Topic

  • QWhat is the difference between a Primary Key and a Foreign Key, and how does that difference show up in an ER diagram?
  • QHow do you represent a many-to-many relationship in a relational database, and can you walk me through creating the junction table?
  • QCan a weak entity have its own Primary Key? Why or why not — and how do you uniquely identify a weak entity's rows?

Frequently Asked Questions

What is the difference between an ER diagram and a database schema?

An ER diagram is the conceptual design — it shows entities, attributes and relationships using shapes and lines, and is tool-agnostic. A database schema is the actual implementation: the SQL CREATE TABLE statements that build real tables in a specific database. The ER diagram comes first and the schema is derived from it. Think of the ER diagram as the architect's plan and the schema as the engineer's technical drawings.

Do I need special software to draw an ER diagram?

No. You can draw a perfectly valid ER diagram on paper, a whiteboard, or a free tool like draw.io (diagrams.net), Lucidchart, or dbdiagram.io. For professional work, tools like MySQL Workbench and pgAdmin can auto-generate ER diagrams from an existing database schema, which is incredibly useful for understanding legacy systems.

What's the difference between a strong entity and a weak entity?

A strong entity can be uniquely identified by its own attributes alone — a Book is uniquely identified by its ISBN whether or not it's linked to anything else. A weak entity cannot exist without a parent entity and doesn't have a full Primary Key on its own — an OrderItem only makes sense in the context of a specific Order, and its identity is partial (ItemNumber within Order #42). In diagrams, weak entities use a double-bordered rectangle and their relationship to the parent uses a double-bordered diamond.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← Previous1NF 2NF 3NF ExplainedNext →Primary Key and Foreign Key
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged