sql-serverinheritancedatabase-designclass-table-inheritance

How can you represent inheritance in a database?


I'm thinking about how to represent a complex structure in a SQL Server database.

Consider an application that needs to store details of a family of objects, which share some attributes, but have many others not common. For example, a commercial insurance package may include liability, motor, property and indemnity cover within the same policy record.

It is trivial to implement this in C#, etc, as you can create a Policy with a collection of Sections, where Section is inherited as required for the various types of cover. However, relational databases don't seem to allow this easily.

I can see that there are two main choices:

  1. Create a Policy table, then a Sections table, with all the fields required, for all possible variations, most of which would be null.

  2. Create a Policy table and numerous Section tables, one for each kind of cover.

Both of these alternatives seem unsatisfactory, especially as it is necessary to write queries across all Sections, which would involve numerous joins, or numerous null-checks.

What are possible solutions for this scenario?


Solution

  • @Bill Karwin describes three inheritance models in his SQL Antipatterns book, when proposing solutions to the SQL Entity-Attribute-Value antipattern. This is a brief overview:

    Single Table Inheritance (aka Table Per Hierarchy Inheritance):

    Using a single table as in your first option is probably the simplest design. As you mentioned, many attributes that are subtype-specific will have to be given a NULL value on rows where these attributes do not apply. With this model, you would have one policies table, which would look something like this:

    +------+---------------------+----------+----------------+------------------+
    | id   | date_issued         | type     | vehicle_reg_no | property_address |
    +------+---------------------+----------+----------------+------------------+
    |    1 | 2010-08-20 12:00:00 | MOTOR    | 01-A-04004     | NULL             |
    |    2 | 2010-08-20 13:00:00 | MOTOR    | 02-B-01010     | NULL             |
    |    3 | 2010-08-20 14:00:00 | PROPERTY | NULL           | Oxford Street    |
    |    4 | 2010-08-20 15:00:00 | MOTOR    | 03-C-02020     | NULL             |
    +------+---------------------+----------+----------------+------------------+
    
    \------ COMMON FIELDS -------/          \----- SUBTYPE SPECIFIC FIELDS -----/
    

    Keeping the design simple is a plus, but the main problems with this approach are the following:

    Concrete Table Inheritance:

    Another approach to tackle inheritance is to create a new table for each subtype, repeating all the common attributes in each table. For example:

    --// Table: policies_motor
    +------+---------------------+----------------+
    | id   | date_issued         | vehicle_reg_no |
    +------+---------------------+----------------+
    |    1 | 2010-08-20 12:00:00 | 01-A-04004     |
    |    2 | 2010-08-20 13:00:00 | 02-B-01010     |
    |    3 | 2010-08-20 15:00:00 | 03-C-02020     |
    +------+---------------------+----------------+
                              
    --// Table: policies_property    
    +------+---------------------+------------------+
    | id   | date_issued         | property_address |
    +------+---------------------+------------------+
    |    1 | 2010-08-20 14:00:00 | Oxford Street    |   
    +------+---------------------+------------------+
    

    This design will basically solve the problems identified for the single table method:

    However this model also comes with a few disadvantages:

    This is how you would have to query all the policies regardless of the type:

    SELECT     date_issued, other_common_fields, 'MOTOR' AS type
    FROM       policies_motor
    UNION ALL
    SELECT     date_issued, other_common_fields, 'PROPERTY' AS type
    FROM       policies_property;
    

    Note how adding new subtypes would require the above query to be modified with an additional UNION ALL for each subtype. This can easily lead to bugs in your application if this operation is forgotten.

    Class Table Inheritance (aka Table Per Type Inheritance):

    This is the solution that @David mentions in the other answer. You create a single table for your base class, which includes all the common attributes. Then you would create specific tables for each subtype, whose primary key also serves as a foreign key to the base table. Example:

    CREATE TABLE policies (
       policy_id          int,
       date_issued        datetime,
    
       -- // other common attributes ...
    );
    
    CREATE TABLE policy_motor (
        policy_id         int,
        vehicle_reg_no    varchar(20),
    
       -- // other attributes specific to motor insurance ...
    
       FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
    );
    
    CREATE TABLE policy_property (
        policy_id         int,
        property_address  varchar(20),
    
       -- // other attributes specific to property insurance ...
    
       FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
    );
    

    This solution solves the problems identified in the other two designs:

    I consider the class table approach as the most suitable in most situations.


    The names of these three models come from Martin Fowler's book Patterns of Enterprise Application Architecture.