Introduction to Single Table Inheritance
What if one database table could represent an entire family of related classes? That is the central idea behind what is single table inheritance.
Single Table Inheritance, often called STI or table-per-hierarchy, stores a parent class and all its child classes in one table. A special column, usually called type, tells your application which class each row represents.
This design appears in object-relational mapping systems such as Rails Active Record. It can simplify queries, reduce joins, and make shared data easy to manage. However, it can also create many empty columns when child classes have different fields.
In this guide, you will learn what is single table inheritance in SQL, how STI works, and how to build a practical example. You will also compare single table inheritance vs class table inheritance, single table inheritance vs polymorphic association, and class table inheritance vs concrete table inheritance.
By the end, you will know when to use STI, when to avoid it, and how to implement it safely in a real project.
Table of Contents
-
What Is Single Table Inheritance?
-
Why Does Single Table Inheritance Matter?
-
Single Table Inheritance Key Facts and Types
-
How to Implement Single Table Inheritance
-
Common Single Table Inheritance Mistakes
-
Expert Tips for Single Table Inheritance
-
Frequently Asked Questions
-
Conclusion
What Is Single Table Inheritance?
Single Table Inheritance is a database design pattern that stores multiple related classes in one database table. The table contains common columns for the parent class, special columns for child classes, and a discriminator column that identifies each row’s specific type.
Suppose you create a Vehicle parent class with two child classes: Car and Motorcycle. Instead of creating separate tables for all three classes, STI creates one vehicles table.
CREATE TABLE vehicles (
id INTEGER PRIMARY KEY,
type VARCHAR(30) NOT NULL,
brand VARCHAR(100),
wheels INTEGER,
number_of_doors INTEGER,
engine_size INTEGER
);The type column identifies the child class:
INSERT INTO vehicles
(type, brand, wheels, number_of_doors, engine_size)
VALUES
('Car', 'Toyota', 4, 4, 1800);
INSERT INTO vehicles
(type, brand, wheels, number_of_doors, engine_size)
VALUES
('Motorcycle', 'Honda', 2, NULL, 600);
The car uses number_of_doors, while the motorcycle uses engine_size. Some fields may remain empty because they apply only to particular child classes.
Think of the table as a school register. Every student appears in one register, but a program column tells you whether each student studies science, arts, or business.
Martin Fowler describes STI as representing an inheritance hierarchy as one table containing fields for the different classes.
Why Does Single Table Inheritance Matter?
STI matters because it gives you a simple way to store related objects while keeping shared data together.
-
Fewer joins: You can retrieve parent and child records from one table without joining several subclass tables.
-
Simple querying: A single query can return every object in the inheritance hierarchy.
-
Shared columns: Common fields such as
name,created_at, andstatusappear once. -
Easy polymorphic behavior: Your application can load each row as the correct child class using the type column.
-
Good framework support: Rails Active Record supports STI by using a column named
typeby default. -
Centralized indexing: Shared search fields can use indexes on one table.
A 2024 database inheritance comparison identifies table-per-hierarchy, table-per-class, and table-per-concrete-class as the three common inheritance mapping strategies. The best option depends on how similar your classes are and how often you query shared data.
STI works especially well when child classes have many common fields and only a small number of special attributes.
Single Table Inheritance Key Facts and Types
The Discriminator Column
The discriminator column tells your application which class should represent a row. Rails uses type by default, but you can configure another inheritance column when needed.
For example:
id | type | name | number_of_doors
1 | Car | Corolla | 4
2 | Motorcycle | CB500 | NULLWhen the application reads row 1, it creates a Car object. When it reads row 2, it creates a Motorcycle object.
Nullable Child Columns
STI places every possible column in one table. This means fields that apply to only one child class usually allow NULL.
That design keeps the schema simple, but too many nullable columns can make the table confusing. It also makes validation more important because the database may not automatically know which fields each subtype requires.
Single Table Inheritance vs Class Table Inheritance
Class Table Inheritance, also called table-per-class or table-per-subclass, creates separate tables for the parent and child classes. A child table usually connects to the parent table through a shared primary key and foreign key.
Concrete Table Inheritance
Concrete Table Inheritance creates a separate complete table for every usable child class. If Car and Motorcycle both inherit from Vehicle, each table repeats shared fields such as brand and created_at.
This approach avoids empty fields, but changes to shared columns may need updates across several tables. It can also make queries across all vehicle types harder.
STI and Polymorphic Associations
STI models different types within one inheritance hierarchy. A polymorphic association connects one model to different unrelated model types.
For example, an Image model might belong to either a Post or a Product. That is a relationship problem, not necessarily an inheritance problem.
Use STI when classes share a genuine “is-a” relationship. Use a polymorphic association when one record can belong to several different models.
How to Implement Single Table Inheritance
1. Identify the Parent and Child Classes
Start with a clear hierarchy. For example, define Payment as the parent class and CreditCardPayment and CashPayment as child classes.
Use STI only when the child classes share meaningful data and behavior. Do not force unrelated models into one hierarchy just to reduce the number of tables.
2. Create One Table
Create a table containing shared fields, the discriminator column, and child-specific fields:
CREATE TABLE payments (
id INTEGER PRIMARY KEY,
type VARCHAR(40) NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
processed_at TIMESTAMP,
card_last_four VARCHAR(4),
cash_received DECIMAL(10, 2)
);The type value identifies the object class. Child-specific columns can remain empty when they do not apply.
3. Add Parent and Child Models
In Rails, your models might look like this:
class Payment < ApplicationRecord
end
class CreditCardPayment < Payment
end
class CashPayment < Payment
end
Rails recognizes the inheritance structure because the table includes a type column. Its official documentation explains that a record such as Firm can be saved in the shared companies table with type = "Firm".
4. Create Child Records
Create records through the child classes:
CreditCardPayment.create(
amount: 75.00,
card_last_four: "4821"
)
CashPayment.create(
amount: 40.00,
cash_received: 50.00
)
Rails automatically writes the correct class name into the type column. This lets the application reload each row as its correct subclass.
5. Query the Parent Class
Query the parent class when you want all payment types:
Payment.allThis returns credit card payments and cash payments. Use a child class when you need only one subtype:
CreditCardPayment.where("amount > ?", 100)6. Add Validation Rules
Child-specific fields need suitable validation:
class CreditCardPayment < Payment
validates :card_last_four, presence: true
end
class CashPayment < Payment
validates :cash_received, presence: true
end
Validation prevents invalid combinations, such as a cash payment without a cash amount.
7. Test Queries and Data Growth
Test parent queries, child queries, updates, deletes, and migrations before release. Check how the table performs when it contains a large number of rows.
Add indexes to columns used often in filtering, especially type, status fields, and timestamps. Review generated SQL when a query becomes slow.
Common Single Table Inheritance Mistakes
-
“STI means the database automatically understands object inheritance.” The database stores rows and columns. Your framework or application interprets the discriminator value and creates the correct object.
-
“Every model should use STI.” STI works best for closely related classes. Unrelated models in one table create confusing columns and difficult validation rules.
-
“The
typecolumn is just a normal text field.” In Rails, atypecolumn activates inheritance behavior. If you intended to store ordinary text, rename the column or change the inheritance configuration. -
“STI eliminates all database design problems.” One table can become very wide, contain many
NULLvalues, and require careful indexes and constraints. -
“STI and polymorphic association mean the same thing.” STI represents subclasses in one hierarchy. A polymorphic association lets one model point to different model types, even when those models do not share inheritance.
Expert Tips for Single Table Inheritance
-
Keep the hierarchy small. A parent with two or three closely related child classes is easier to maintain than a large, deeply nested structure.
-
Choose the discriminator carefully. Use stable type names and avoid changing class names casually because stored values may depend on them.
-
Add subtype validation. Make child-specific fields mandatory only for the relevant subclass.
-
Index real query paths. Index
typetogether with frequently filtered columns when your workload needs faster subtype queries. -
Measure before changing patterns. Compare STI with class table inheritance using realistic data, queries, joins, and expected growth.
Frequently Asked Questions
What is single inheritance with an example?
Single inheritance means one child class inherits from one parent class. For example, Car can inherit shared properties and methods from Vehicle, such as brand and start(). In a database, Single Table Inheritance can store both Car and other vehicle subclasses in one table, while a type column identifies the specific child class represented by each row.
What is single table inheritance in Rails?
Single Table Inheritance in Rails stores a model hierarchy in one database table. Rails uses a type column by default to identify subclasses. If CreditCardPayment inherits from Payment, Rails saves the record in the payments table and stores CreditCardPayment in the type column. Rails then reloads that row as the correct subclass.
What are the four types of inheritance?
In object-oriented programming, the commonly taught four types are single inheritance, multiple inheritance, multilevel inheritance, and hierarchical inheritance. Some courses also discuss hybrid inheritance, so terminology can vary. In database mapping, the main patterns are usually STI, class table inheritance, and concrete table inheritance rather than the four object-oriented categories.
Conclusion
Single Table Inheritance stores an inheritance hierarchy in one table and uses a discriminator column to identify each subtype. Its main advantages are simpler queries, fewer joins, centralized shared fields, and strong framework support.
The main trade-off is a wider table with nullable columns for fields that apply only to certain child classes. You should compare STI with class table inheritance, concrete table inheritance, and polymorphic associations before choosing a design.
Start today by modeling a small hierarchy such as Vehicle, Car, and Motorcycle. Create the table, add the discriminator column, and test parent and child queries. Which inheritance strategy fits your next database project?
