From what I understand of your spreadsheet and your description, this is the normalized logical ERD for your data:
![Logical ERD](https://i.stack.imgur.com/tyQJK.jpg)
Now, based on how many GAP_CATEGORY
records you have as a rule, and how many FORCE_SOURCE
records you have, you might decide to do some denormalization. You have to decide what is easier to build, maintain and use and makes more sense in your application.
I solved this (for single inheritance) like so:
each extension of a class inherits, as its primary key the base classes primary key and always targets with its fk the immediate base class:
class A
class B extends A
class C extends B
A(id,p1)
B(id,p1,p2)
B(id,p1,p2,p3)
Table A:
id: primary key
p1: sth
Table B:
id: primary key + foreign key -> A.id
p2: sth
Table C:
id: primary key + foreign key -> B.id
p3: sth
this would reqiure a join to completely retrieve B or C.
alternatively you can have each extension mirror the properties of the base classes:
Table A:
id: primary key
p1: sth
Table B:
id: primary key + foreign key -> A.id
p1: sth
p2: sth
Table C:
id: primary key + foreign key -> B.id
p1: sth
p2: sth
p3: sth
here you can retrieve any class without joins but data is duplicated and has to be kept consistent.
which solution you can use strongly depends on how you want to retrieve the data and if you can live with the overhead of the join in version 1 or overhead of writing data multiple times in version 2.
I also added a meta-column to the table to know if a row does have extensions or not which is useful for a some queries:
Table A:
id: primary key
p1: sth
class: A,B,C
Table B:
id: primary key
p2: sth
class: B,C
Table C:
id: primary key
p2: sth
class: C
Best Answer
Here is a possible solution. Navigating the structure from top to bottom may be a little gross but maybe the app side objects can handle it.
I think you can build this out using 2 tables
dbo.Device
anddbo.Combiner
. Each table has a column that references who its parent object is, regardless of whether it is adevice
or acombiner
. We can then use a couple different types ofconstraints
and a couple oftriggers
to ensure that the data follows the path you are looking to use.Table Structures (now your objects probably have more data points than just a
name
but for the sake of simplicity I am only going to keep track of aname
)dbo.Device
dbo.Combiner
Each table holds who its parent object is via
ParentDeviceID
orParentCombinerID
. TheUnique Filtered Index
onParentDeviceID
helps ensure that a givenDevice
can only be the parent of one object (AUnique Constraint
would stop multiple rows with aNULL
value). It isn't guaranteed so that is why we are going to use some triggers down the road. The twoCHECK Constraint
s help ensure that any given object can only have 1 parent object instead of having both aDevice
andCombiner
as a parent. TheForeign Key
s ensure that the values stored inParentDeviceID
andParentCombinerID
are valid values for those two tables.Trigger
sWe can insert a
Trigger
on eachtable
. TheseTrigger
s need to ensure:insert
/update
of adbo.Device
record a value stored onParentDeviceID
is not already ondbo.Combiner.ParentDeviceID
Insert
/Update
of adbo.Combiner
record a value stored onParentDeviceID
is not already ondbo.Device.ParentDeviceID
As an alternative this StackOverflow question apperently give the same or similar effect with a
Unique Index
on aView
. If you want to avoidTriggers
than this may be a solution for you. https://stackoverflow.com/questions/16314372/ms-sql-server-cross-table-constraint. I am not familiar with this approach but maybe it is a better solution for you.Examples
Implementing the 1st Example provided (just 3 devices in a chain) would look something like this. Just 3 records in
dbo.Device
:Implementing the 2nd Example provided (1 Device --> 1 Combiner --> 3 Devices) would look something like this. 4 Records in
dbo.Device
and 1 Record indbo.Combiner
.One more, slightly more complicated example to display all the relationship options:![Slightly More Involved Example](https://i.stack.imgur.com/prHZt.jpg)
Hopefully this give you all you need from the database side. Again I think this will be complicated on the App side navigating the table structures. But the database should keep the relationships the way you want to.