The basic idea is to separate hierarchy of an organizational chart from actuals teams.
The Level
table looks like
LevelID | LevelType
-------------------
1 League
2 Division
3 Conference
4 Team_Slot
- Team_Slot is a "pigeon-hole" for an actual team
to fill-in over time.
Few more notes:
Alternate Key (PositionID, LevelID)
is propagated into TeamPosition
in order to allow a check constraint LevelID = 4 -- actual teams can fill-in only Team_Slots.
Team can not change Team_Slot within one season.
For simplicity, the hierarchy is here modelled with the levelled-adjacency-list; not most efficient for SQL hierarchies, but good enough for an explanation.
For more efficient hierarchy models, see Celko's book or just google 'SQL hierarchies'.
Do not know if this will be useful to you because it requires quite a few changes, but the problem is interesting, so I'll try.
These would be the major changes
- Using the tree closure instead of the adjacency list for the reference hierarchy. The closure table contains paths form each parent to all of it descendants, so all possible parent-child combinations are exposed.
Note that with the tree closure, each ancestor node points to itself as a descendant, meaning that in CaseProperty
recursion stops on ID = ParentID
instead on ParentID is NULL
It is not clear to me is a parent allowed to be any ancestor or just the one first step up. The closure table exposes ancestor and all descendants, so Level Difference
is added to the TreeClosure
, which is sub-typed as AllowedCombos
for LevelDifference in (0,1)
.
Propagating AK {PropertyID, PropertyTypeID}
instead of just PropertyID
Using composite key in CaseProperty
Here are main constraints from the model to clarify relationships (you may need to modify syntax)
ALTER TABLE Property ADD
CONSTRAINT PK_PR PRIMARY KEY (PropertyID)
, CONSTRAINT AK1_PR UNIQUE (PropertyID ,PropertyTypeID)
, CONSTRAINT FK1_PR FOREIGN KEY (PropertyTypeID)
REFERENCES PropertyType(PropertyTypeID)
;
ALTER TABLE TreeClosure ADD
CONSTRAINT PK_TC PRIMARY KEY (AncestorID ,DescendantID ,AncestorTypeID ,DescendantTypeID)
, CONSTRAINT FK1_TC FOREIGN KEY (AncestorID ,AncestorTypeID)
REFERENCES Property(PropertyID ,PropertyTypeID)
, CONSTRAINT FK2_TC FOREIGN KEY (DescendantID ,DescendantTypeID)
REFERENCES Property(PropertyID ,PropertyTypeID)
;
ALTER TABLE CaseProperty ADD
CONSTRAINT PK_CP PRIMARY KEY (CaseID, PropertyID, PropertyTypeID)
, CONSTRAINT FK1_CP FOREIGN KEY (CaseID)
REFERENCES Case(CaseID)
, CONSTRAINT FK2_CP FOREIGN KEY (PropertyID ,PropertyTypeID)
REFERENCES Property(PropertyID ,PropertyTypeID)
, CONSTRAINT FK4_CP FOREIGN KEY (ParentCaseID ,ParentPropertyID ,ParentPropertyTypeID)
REFERENCES CaseProperty(CaseID ,PropertyID ,PropertyTypeID)
, CONSTRAINT FK5_CP FOREIGN KEY (ParentPropertyID ,PropertyID , ParentPropertyTypeID ,PropertyTypeID)
REFERENCES AllowedCombos(AncestorID ,DescendantID , AncestorTypeID ,DescendantTypeID)
;
Best Answer
You can add an attribute to
MEMBER
that defines the type. Example:I've heard that some DBMS support
CHECK CONSTRAINTS
withSELECT
and for such you can add a CHECK constraint in each sub table that verifies the typeFor DBMS that does not support that you can add a unique constraint in MEMBER:
and "inherit" the MEMBER_TYPE attribute to the sub tables:
Another idea is to add validation triggers to the sub tables that validate that the member is of correct type before inserting or updating.