HiveBrain v1.2.0
Get Started
← Back to all entries
patternsqlMinor

How should an object that can reference an object of multiple types be modeled in a relational schema?

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
typescanreferencehowrelationalthatmultipleshouldmodeledobject

Problem

Apologies for the obscure question, it may make more sense with a concrete example:

In my application, I may create a portfolio that contains a set of projects. However, I may also add a 'sub-portfolio' nested within a portfolio (which then may contain projects). Currently, I have my schema drawn out the following way:

+------------+    +-----------------------+    +------------+
| Portfolios |    | Portfolio_project_map |    | Projects   |
+------------+    +-----------------------+    +------------+
|   port_id  |    |   parent_id           |    |   proj_id  |
+------------+    +-----------------------+    +------------+
|            |    |   child_id            |    |            |
                  +-----------------------+
                  |   child_is_portfolio  |
                  +-----------------------+


This seems extremely ugly. What is the proper way of modeling such a relationship in a relational database? Or is there really not one, and such details should be closely enforced in the business logic? Is it even possible to specify foreign key constraints on the Portfolio_project_map such that child_id references either port_id or proj_id?

Also, what exactly is such a problem called? I found 'one-to-either' referenced in a couple places, but I have a feeling that's not completely correct?

Solution

If my memory serves me well, 'One to either' is called Polymorphic Association. Usually, it can be resolved by using a common parent table (index definitions are skipped for simplicity):

PortfolioParent (id int not null PRIMARY KEY,
portfolio_type char(1) not null,
CONSTRAINT UQ_PORTFOLIO_PARENT UNIQUE (id,portfolio_type),
-- unique constraint may seem redundant, but it's required so other tables can refer to 
-- (id,portfolio_type)
CONSTRAINT CHK_PORTFOLIO_PARENT_TYPE CHECK portfolio_type IN ('P','S')) --'P' - portfolio,'S'-subportfolio

); 

Portfolio(id int not null PRIMARY KEY,
parent_id int not null,
portfolio_type char(1) not null,
--portfolio attributes
CONSTRAINT CHK_PORTFOLIO_TYPE CHECK (portfolio_type ='P'),
CONSRTRAINT FK_PORTFOLIO_PORT_PARENT FOREIGN KEY (parent_id,portfolio_type) 
  REFERENCES PortfolioParent(id,portfolio_type));

SubPortfolio (id int not null PRIMARY KEY,
parent_id int not null,
portfolio_type char(1) not null,
-- sub-portfolio attributes, portfolio_id
CONSTRAINT CHK_SUBPORTFOLIO_TYPE CHECK (portfolio_type ='S'),
CONSRTRAINT FK_SUBPORTFOLIO_PORT_PARENT FOREIGN KEY (parent_id,portfolio_type)
   REFERENCES  PortfolioParent(id,portfolio_type));

Project(id int not null PRIMARY KEY,
parent_id int not null,
--project attributes
CONSTRAINT FK_Project_PARENT FOREIGN KEY(parent_id) REFERENCES PortfolioParent(id));

Code Snippets

PortfolioParent (id int not null PRIMARY KEY,
portfolio_type char(1) not null,
CONSTRAINT UQ_PORTFOLIO_PARENT UNIQUE (id,portfolio_type),
-- unique constraint may seem redundant, but it's required so other tables can refer to 
-- (id,portfolio_type)
CONSTRAINT CHK_PORTFOLIO_PARENT_TYPE CHECK portfolio_type IN ('P','S')) --'P' - portfolio,'S'-subportfolio

); 

Portfolio(id int not null PRIMARY KEY,
parent_id int not null,
portfolio_type char(1) not null,
--portfolio attributes
CONSTRAINT CHK_PORTFOLIO_TYPE CHECK (portfolio_type ='P'),
CONSRTRAINT FK_PORTFOLIO_PORT_PARENT FOREIGN KEY (parent_id,portfolio_type) 
  REFERENCES PortfolioParent(id,portfolio_type));

SubPortfolio (id int not null PRIMARY KEY,
parent_id int not null,
portfolio_type char(1) not null,
-- sub-portfolio attributes, portfolio_id
CONSTRAINT CHK_SUBPORTFOLIO_TYPE CHECK (portfolio_type ='S'),
CONSRTRAINT FK_SUBPORTFOLIO_PORT_PARENT FOREIGN KEY (parent_id,portfolio_type)
   REFERENCES  PortfolioParent(id,portfolio_type));

Project(id int not null PRIMARY KEY,
parent_id int not null,
--project attributes
CONSTRAINT FK_Project_PARENT FOREIGN KEY(parent_id) REFERENCES PortfolioParent(id));

Context

StackExchange Database Administrators Q#15511, answer score: 3

Revisions (0)

No revisions yet.