Bridging the Gap: Object-Oriented Inheritance in Relational Databases
- Relational databases are flat: They store data in tables with rows and columns. Inheritance creates hierarchies, which aren't built-in.
There are two main approaches to model inheritance in a SQL Server database:
-
Multiple Tables:
- Create separate tables for the parent class and each child class.
- The child table inherits all the columns from the parent and adds its own specific columns.
- This approach is simple but can lead to data duplication if many child classes share attributes with the parent.
- Adding a new child class requires creating a new table.
-
Single Table:
- Create a single table for all classes (parent and children).
- Include all possible columns from all classes in the table.
- Add a column to differentiate between classes (often called a discriminator).
- Most columns for child-specific data will be null for parent class entries.
- This approach avoids data duplication but can waste space and require complex queries to filter data for specific classes.
Neither approach is perfect. The best choice depends on your specific needs and how often you expect the class structure to change.
Here are some additional factors to consider for database design:
- Normalization: This is a general database design principle that aims to reduce data duplication. While inheritance might suggest some duplication is okay, excessive redundancy can lead to maintenance problems.
- Performance: Complexity in either approach can slow down queries. Multiple tables might require joining them to get complete data, while a single table might have many null values to manage.
-- Parent table (People)
CREATE TABLE People (
PersonID int PRIMARY KEY,
Name varchar(50) NOT NULL,
Age int
);
-- Child table inheriting from People (Students)
CREATE TABLE Students (
PersonID int PRIMARY KEY FOREIGN KEY REFERENCES People(PersonID),
Major varchar(50)
);
-- Child table inheriting from People (Teachers)
CREATE TABLE Teachers (
PersonID int PRIMARY KEY FOREIGN KEY REFERENCES People(PersonID),
Subject varchar(50)
);
In this example, Students and Teachers inherit the PersonID and Name columns from People. They each have an additional column specific to their class (Major for Students, Subject for Teachers).
CREATE TABLE People (
PersonID int PRIMARY KEY,
Name varchar(50) NOT NULL,
Age int,
Major varchar(50), -- Might be null for non-students
Subject varchar(50) -- Might be null for non-teachers
Discriminator char(10) CHECK (Discriminator IN ('Student', 'Teacher', 'Other'))
);
Here, all columns from both child classes (Major and Subject) are included in the People table. We add a Discriminator column to identify the class type (Student, Teacher, or Other for generic People entries). Most rows will have null values in some columns depending on the class type.
- Computed Columns:
This approach uses a single table for all classes and a computed column (a column whose value is derived from other columns) to represent the discriminator. This can be helpful if the number of child classes is limited and their distinction is simple.
Here's an example with a computed discriminator for a Person table with Students and Employees:
CREATE TABLE People (
PersonID int PRIMARY KEY,
Name varchar(50) NOT NULL,
Age int,
-- Other common columns
Discriminator AS (CASE WHEN Major IS NOT NULL THEN 'Student' WHEN JobTitle IS NOT NULL THEN 'Employee' ELSE NULL END) PERSISTED
);
CREATE TABLE StudentDetails ( -- Optional table for student specific data
PersonID int PRIMARY KEY FOREIGN KEY REFERENCES People(PersonID),
Major varchar(50)
);
CREATE TABLE EmployeeDetails ( -- Optional table for employee specific data
PersonID int PRIMARY KEY FOREIGN KEY REFERENCES People(PersonID),
JobTitle varchar(50)
);
This approach avoids some data duplication but can become complex if the logic for determining the discriminator gets too involved.
- Entity-Attribute-Value (EAV):
This is a more generic approach where data is stored in three tables:
- Entity: Stores unique identifiers for each record.
- Attribute: Stores all possible attributes across all classes.
- Value: Links entities to attributes and holds the specific value for that entity-attribute pair.
This approach offers flexibility for a wide variety of data structures but can be complex to manage and query efficiently. It's generally not recommended for typical use cases.
- Document Databases:
If your data model is very hierarchical and constantly evolving, consider document databases like MongoDB. These databases store data in JSON-like documents which can naturally represent inheritance structures. However, document databases have different strengths and weaknesses compared to relational databases like SQL Server.
sql-server inheritance database-design