Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Implementing a supertype/subtype model means converting an enhanced entity–relationship (EER) hierarchy into tables, keys, constraints, and queries. The three practical strategies are table per hierarchy (TPH), table per type (TPT), and table per concrete type (TPC). Choose among them only after documenting whether specialization is total or partial, disjoint or overlapping, and whether the type distinction is a stable business fact rather than an application label.

For a small, stable, mostly disjoint hierarchy, TPH is often the simplest default. TPT is a better fit when subtype attributes and constraints are substantial and should remain in separate, normalized tables. TPC can make concrete-type reads simple, but duplicates inherited data and complicates global identifiers. Microsoft documents these mappings for EF Core, while Oracle SQL Developer Data Modeler exposes comparable engineering choices (EF Core inheritance mapping; Oracle Data Modeler guide).

What are supertypes and subtypes?

A supertype is a generalized entity that owns attributes and relationships common to several categories. A subtype inherits the supertype’s identity and common properties, then adds attributes, relationships, or rules of its own. Specialization is the top-down process of refining a general entity; generalization is the bottom-up process of factoring shared properties into a supertype.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Person
├── Student
└── Employee
    └── Manager

Inheritance can span multiple levels. A manager inherits employee and person properties, not merely the attributes declared directly on manager. Oracle’s object-relational documentation describes similar multi-level subtype inheritance, but native Oracle object types are a separate feature from ordinary relational table mapping (Oracle object-type inheritance).

Validate the model before designing tables

Write these decisions down before choosing a physical strategy:

  • Completeness: In a total (complete) specialization every supertype row belongs to at least one subtype. In a partial specialization, a bare supertype row is valid.
  • Disjointness: In a disjoint hierarchy an instance belongs to one subtype only. In an overlapping hierarchy it may belong to several, such as a person who is both an employee and a customer.
  • Depth: Decide whether subtypes can themselves have subtypes. Every additional level adds joins, discriminator values, or migration work.
  • Identity: Usually the subtype represents the same real-world object, so its key is the supertype key, not a second unrelated identifier.

Use inheritance when categories share a stable identity and common meaning but have genuinely different properties or rules. A status column is usually better when the difference is only a lifecycle state. Roles, a many-to-many category table, or composition are better when categories overlap, change independently, or can be added by users.

Worked example

Assume Person has person_id, first_name, and last_name. Students additionally require student_number and major; employees require employee_number and hire_date. The examples below assume a disjoint hierarchy unless stated otherwise.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Strategy 1: Single table (table per hierarchy)

TPH stores every hierarchy member in one table and uses a discriminator to identify the concrete type. EF Core uses TPH by default and allows the discriminator name and values to be configured (Microsoft EF Core documentation).

CREATE TABLE person (
    person_id       BIGINT PRIMARY KEY,
    person_type     VARCHAR(20) NOT NULL,
    first_name      VARCHAR(100) NOT NULL,
    last_name       VARCHAR(100) NOT NULL,
    student_number  VARCHAR(30),
    major           VARCHAR(100),
    employee_number VARCHAR(30),
    hire_date       DATE,
    CONSTRAINT ck_person_type
        CHECK (person_type IN ('PERSON', 'STUDENT', 'EMPLOYEE')),
    CONSTRAINT ck_student_fields
        CHECK (person_type <> 'STUDENT' OR
               (student_number IS NOT NULL AND major IS NOT NULL
                AND employee_number IS NULL AND hire_date IS NULL)),
    CONSTRAINT ck_employee_fields
        CHECK (person_type <> 'EMPLOYEE' OR
               (employee_number IS NOT NULL AND hire_date IS NOT NULL
                AND student_number IS NULL AND major IS NULL))
);

For a total, disjoint hierarchy, make the discriminator NOT NULL and allow only concrete values, such as STUDENT and EMPLOYEE. Include PERSON only when the supertype itself is instantiable. Conditional checks are essential: a discriminator alone does not make subtype columns valid.

Strengths and weaknesses

  • One row represents one entity, making identity, polymorphic foreign keys, and supertype-wide queries straightforward.
  • Concrete reads require no inheritance joins.
  • Subtype-only columns are nullable for other types, and a large hierarchy can become wide and sparse.
  • Complex overlapping rules are awkward with a single discriminator.

For overlapping membership, use separate flags only for a very small, stable set of roles. A membership table scales better:

CREATE TABLE person_subtype (
    person_id    BIGINT NOT NULL REFERENCES person(person_id),
    subtype_code VARCHAR(30) NOT NULL,
    PRIMARY KEY (person_id, subtype_code)
);

Strategy 2: Table per type (table per child)

TPT stores common columns in the supertype table and subtype columns in child tables. Each child primary key is also a foreign key to the parent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
CREATE TABLE person (
    person_id  BIGINT PRIMARY KEY,
    first_name VARCHAR(100) NOT NULL,
    last_name  VARCHAR(100) NOT NULL
);

CREATE TABLE student (
    person_id      BIGINT PRIMARY KEY
                   REFERENCES person(person_id) ON DELETE CASCADE,
    student_number VARCHAR(30) NOT NULL,
    major          VARCHAR(100) NOT NULL
);

CREATE TABLE employee (
    person_id       BIGINT PRIMARY KEY
                    REFERENCES person(person_id) ON DELETE CASCADE,
    employee_number VARCHAR(30) NOT NULL,
    hire_date       DATE NOT NULL
);

Subtype fields can be genuinely NOT NULL, and subtype-specific relationships are natural. Creating a student must be atomic:

BEGIN;
INSERT INTO person (person_id, first_name, last_name)
VALUES (1001, 'Ava', 'Morgan');
INSERT INTO student (person_id, student_number, major)
VALUES (1001, 'S-1001', 'Physics');
COMMIT;

A concrete query joins the tables:

SELECT p.person_id, p.first_name, p.last_name,
       s.student_number, s.major
FROM person AS p
JOIN student AS s ON s.person_id = p.person_id
WHERE p.person_id = 1001;

A foreign key guarantees that a child has a parent; it does not guarantee that every parent has a child or that a parent appears in only one child table. Totality and disjointness therefore require controlled write procedures, triggers, a membership table, or another database-specific mechanism. TPT also adds joins to polymorphic queries, and Microsoft warns that its generated SQL is commonly more complex and can be slower than TPH (EF performance white paper).

Strategy 3: Table per concrete type (table per leaf)

TPC creates a complete table for each concrete subtype, including inherited columns. Abstract supertypes have no table.

CREATE TABLE student (
    person_id      BIGINT PRIMARY KEY,
    first_name     VARCHAR(100) NOT NULL,
    last_name      VARCHAR(100) NOT NULL,
    student_number VARCHAR(30) NOT NULL,
    major          VARCHAR(100) NOT NULL
);

CREATE TABLE employee (
    person_id       BIGINT PRIMARY KEY,
    first_name      VARCHAR(100) NOT NULL,
    last_name       VARCHAR(100) NOT NULL,
    employee_number VARCHAR(30) NOT NULL,
    hire_date       DATE NOT NULL
);

Concrete reads avoid joins, but common data is duplicated. A supertype query needs a union:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
SELECT person_id, first_name, last_name, 'STUDENT' AS person_type
FROM student
UNION ALL
SELECT person_id, first_name, last_name, 'EMPLOYEE' AS person_type
FROM employee;

Independent identity columns can generate the same number in different tables. If identifiers are global across the hierarchy, use a shared sequence, application-generated UUIDs, a central identifier table, or non-overlapping numeric ranges. EF Core documents this key-generation issue for TPC (EF Core inheritance strategies).

Exclusive arcs and separate membership

A diagram showing one supertype with exclusive links to child tables does not automatically become a database constraint. An exclusive-arc design is useful when subtype tables are large or have many relationships, but explicitly define whether a parent may have no child, exactly one child, or several. Oracle Data Modeler documents variants of these engineering choices, including identifying relationships and complete hierarchies (Data Modeler documentation).

For overlapping or dynamic categories, an explicit association such as person_subtype or role tables is generally clearer than adding flags or child tables indefinitely.

How to choose

Requirement Usually favors
Simplest schema and frequent whole-hierarchy queries TPH
Many sparse subtype attributes TPT or TPC
Strict subtype-specific NOT NULL rules TPT or TPC
Concrete reads dominate and duplicated common data is acceptable TPC
Deep hierarchy or globally unique identity Usually TPH or TPT
Overlapping membership TPT or roles/association tables
Frequent addition of user-defined types Category or extension model

There is no universally fastest strategy. Row width, selectivity, indexes, data volume, and access patterns matter. Measure representative workloads before committing; Microsoft’s performance guidance explicitly recommends doing so (EF Core performance modeling).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Implementation workflow

  1. Specify the hierarchy: list the supertype, subtypes, completeness, disjointness, abstract/concrete status, and subtype relationships.
  2. Confirm identity: decide whether all members share one key domain.
  3. Choose the strategy: compare reads, writes, nullability, joins, schema evolution, ORM support, and constraint enforceability.
  4. Build referential integrity: for TPT, make each child primary key a foreign key to the parent.
  5. Enforce membership: use a constrained discriminator, child existence, or an explicit membership table.
  6. Add targeted indexes: index discriminator and subtype columns only when query patterns justify them.
  7. Expose views when useful: a unified view can simplify reports over TPT or TPC, but it does not fix write-path integrity.
  8. Test invalid states: missing required subtype, orphan child, incompatible discriminator, duplicate disjoint membership, failed cascade, duplicate TPC key, unknown type, and concurrent subtype creation.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

EF Core mapping notes

EF Core supports all three relational strategies. TPH can be configured as:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasDiscriminator<string>("person_type")
        .HasValue<Person>("person")
        .HasValue<Student>("student")
        .HasValue<Employee>("employee");
}

TPT maps each type to a table:

modelBuilder.Entity<Person>().ToTable("person");
modelBuilder.Entity<Student>().ToTable("student");
modelBuilder.Entity<Employee>().ToTable("employee");

TPC uses UseTpcMappingStrategy() on the root and maps concrete types to full tables. EF Core 5 introduced TPT support and EF Core 7 introduced TPC support (EF Core 7 what’s new). Verify generated migrations, discriminator checks, cascade behavior, indexes, and SQL plans rather than assuming the object model enforces every database rule.

Common failures and recovery

TPH becomes unmanageably sparse

Split large subtype families into TPT, compose optional detail tables, or replace volatile categories with roles. Keep TPH for a stable common core.

TPT makes polymorphic reads slow

Use projections and covering indexes, add read-optimized views or materialized projections, and benchmark TPH against production-like data before redesigning.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

TPC creates duplicate IDs

Adopt UUIDs, a shared sequence, a central identifier service, or explicitly redefine IDs as table-local.

Disjointness exists only in the diagram

Add a discriminator or membership table and centralize writes in a procedure or service; foreign keys alone are insufficient.

A lifecycle state was modeled as inheritance

Use status history or temporal membership when records move between states and historical classification matters. Structural subtype changes can otherwise lose data or make migrations brittle.

Inheritance is not the only design

  • Composition: a common entity owns optional detail tables.
  • Roles: separate PersonEmployeeRole and PersonCustomerRole tables for independent capabilities.
  • Category association: a many-to-many entity/category model for overlapping or user-defined classifications.
  • Status: a constrained column or status-history table for lifecycle states.
  • Extension attributes: a carefully governed extension model when attributes are genuinely dynamic.

The correct physical design is the simplest one that preserves the business rules and matches dominant workload patterns. Do not choose a mapping merely because it resembles the application’s class hierarchy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.