database design
inheritance models
database inheritance
data modeling
object-relational mapping

How do you effectively model inheritance in a database?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Relational databases do not have object-oriented inheritance built in, so every inheritance design is a mapping choice rather than a native feature. The effective model depends on what you optimize for: query simplicity, normalization, update discipline, or compatibility with your ORM.

The Three Common Strategies

Most database inheritance discussions reduce to three patterns:

  • single-table inheritance
  • class-table inheritance
  • concrete-table inheritance

Each is valid. The mistake is acting as if one pattern is universally best.

Single-Table Inheritance

In single-table inheritance, every subtype lives in one table and a discriminator column tells you which kind of row it is.

sql
1CREATE TABLE vehicle (
2    id           BIGINT PRIMARY KEY,
3    vehicle_type VARCHAR(20) NOT NULL,
4    make         VARCHAR(100) NOT NULL,
5    doors        INTEGER NULL,
6    payload_kg   INTEGER NULL
7);

A car row might use doors, while a truck row might use payload_kg. Columns that do not apply to a subtype are left NULL.

This pattern is attractive when:

  • the hierarchy is small
  • reads are frequent
  • you want simple queries without joins

Its biggest drawback is sparsity. As the hierarchy grows, the table accumulates many subtype-specific columns and lots of NULL values.

Class-Table Inheritance

Class-table inheritance keeps shared fields in a base table and subtype fields in separate child tables.

sql
1CREATE TABLE vehicle (
2    id   BIGINT PRIMARY KEY,
3    make VARCHAR(100) NOT NULL
4);
5
6CREATE TABLE car (
7    id    BIGINT PRIMARY KEY,
8    doors INTEGER NOT NULL,
9    FOREIGN KEY (id) REFERENCES vehicle(id)
10);
11
12CREATE TABLE truck (
13    id         BIGINT PRIMARY KEY,
14    payload_kg INTEGER NOT NULL,
15    FOREIGN KEY (id) REFERENCES vehicle(id)
16);

This is more normalized and keeps subtype-specific fields where they belong. It is a strong fit when the hierarchy is stable and data integrity matters more than having one-table reads.

The tradeoff is query complexity. Fetching a full subtype often requires joins, and polymorphic queries across the hierarchy can become heavier.

Concrete-Table Inheritance

Concrete-table inheritance gives each subtype its own full table, including columns duplicated from the conceptual base type.

sql
1CREATE TABLE car (
2    id    BIGINT PRIMARY KEY,
3    make  VARCHAR(100) NOT NULL,
4    doors INTEGER NOT NULL
5);
6
7CREATE TABLE truck (
8    id         BIGINT PRIMARY KEY,
9    make       VARCHAR(100) NOT NULL,
10    payload_kg INTEGER NOT NULL
11);

This avoids joins and avoids sparse columns, but it duplicates shared fields such as make. That duplication increases maintenance cost when shared attributes evolve.

This pattern is best when subtypes are truly separate in most queries and you rarely need polymorphic operations across the whole hierarchy.

How to Choose Effectively

A useful selection rule is:

  • choose single-table inheritance for small hierarchies and fast simple reads
  • choose class-table inheritance when normalization and subtype integrity matter most
  • choose concrete-table inheritance when subtype tables are operationally separate and polymorphic queries are rare

Also ask how the application reads the data.

If most requests say "load this thing regardless of subtype," one-table reads may be attractive.

If most requests say "load trucks as trucks and cars as cars," separate subtype tables may be cleaner.

ORM Considerations

ORMs often expose these same patterns directly. For example, JPA and Hibernate support variants of all three strategies.

That support is helpful, but the database design should still be driven by data access patterns, not only by which annotation looks easiest today. ORMs can make a poor schema easier to write, but they do not remove the underlying tradeoffs.

A common failure mode is choosing inheritance because the object model has inheritance, even when the database would be simpler with composition or separate related tables.

When Not to Model Inheritance at All

Sometimes the best answer is not inheritance. If the shared fields are minimal and the behaviors are operationally different, forcing a hierarchy can create more complexity than it saves.

Likewise, if subtype-specific attributes are highly dynamic, a different modeling strategy such as composition, related detail tables, or carefully designed JSON columns may fit better than a rigid inheritance map.

Common Pitfalls

Choosing single-table inheritance for a large, fast-growing hierarchy often leads to bloated tables full of mostly irrelevant columns.

Choosing class-table inheritance without considering query cost can create join-heavy read paths that are harder to tune.

Choosing concrete-table inheritance and then needing frequent cross-type reporting leads to repetitive union queries and duplicated logic.

The biggest conceptual mistake is assuming the database must mirror the object model exactly. Relational design and object design serve related but different goals.

Summary

  • database inheritance is a modeling choice, not a built-in relational feature
  • single-table inheritance favors simple reads but creates sparse schemas
  • class-table inheritance is normalized but requires more joins
  • concrete-table inheritance avoids joins at the subtype level but duplicates shared columns
  • choose the pattern based on query behavior, integrity needs, and operational simplicity rather than on ORM defaults alone

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.