If the relationship is strictly hierarchical (i.e. a portfolio can have several sub-portfolios or projects, but a project cannot appear in more than one sub-portfolio) then you can model portfolio and project using a subtype pattern, e.g.
-- === PortfolioItem table ===========================================
-- Supertype table that records the relationships between portfolio
-- items.
create table PortfolioItem (
PortfolioItemID int not null
,PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null -- either 'PROJ' or 'PORT'
,ParentItemID int -- Null for root
)
go
alter table PortFolioItem
add constraint PK_PortfolioItem
primary key nonclustered (PortfolioItemID)
go
alter table PortfolioItem
add constraint UQ_PortfolioItem
unique nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Parent-child relationship
--
alter table PortfolioItem
add constraint FK_PortfolioItem_Parent
foreign key (ParentItemID)
references PortfolioItem
go
-- === PortfolioSubType ===============================================
-- This subclass table joins against the unique identifier but the
-- check constraint restricts it to joining against 'portfolio' nodes
--
create table PortfolioSubType (
PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null
-- Portfolio attributes
)
alter table PortfolioSubType
add constraint PK_PortfolioSubType
primary key nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Cab only join against portfolio parent items
--
alter table PortfolioSubType
add constraint CK_Portfolio_Type
check (PortfolioItemType = 'PORT')
go
alter table PortfolioSubType
add constraint FK_PortfolioSubType_SuperType
foreign key (PortfolioItemRef, PortfolioItemType)
references PortfolioItem (PortfolioItemRef, PortfolioItemType)
go
-- === ProjectSubType =================================================
-- This subclass table has the project specific items and a check
-- constraint that prevents it from joining against parent nodes
-- that represent portfolios
--
create table ProjectSubType (
PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null
-- Project attributes
)
alter table ProjectSubType
add constraint PK_ProjectSubType
primary key nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Check constraint restricts this to projects
--
alter table ProjectSubType
add constraint CK_Portfolio_Type
check (PortfolioItemType = 'PROJ')
go
alter table ProjectSubType
add constraint FK_ProjectSubType_SuperType
foreign key (PortfolioItemRef, PortfolioItemType)
references PortfolioItem (PortfolioItemRef, PortfolioItemType)
go
You could enforce an integrity rule that prevents a project node from having children with a trigger similar to the following:
-- === Trigger to enforce integrity ===================================
-- The trigger prevents project nodes from having children.
--
create trigger ProjectNodeIntegrity
on PortfolioItem
for insert, update
as
if exists
(select 1
from PortfolioItem p_i
join inserted i
on i.ParentItemID = p_i.PortfolioItemID
and p_i.PortfolioItemType = 'PROJ') begin
raiserror ('Only portfolios may have children', 16, 1)
rollback transaction
return
end
go
This will bounce attempts to insert a child under a project node.