views:

51

answers:

2

I have the following tables:

Section and Content

And I want to relate them.


My current approach is the following table:

relation table

In which I would store

  • Section to Section
  • Section to Content
  • Content to Section
  • Content to Content


Now, while I clearly can do that by adding a pair of fields that indicate whether the source is a section or a content, and whether the target is a section or a content, I'd like to know if there's a cleaner way to do this. and if possible using just one table for the relationship, which would be the cleanest in my opinion. I'd also like the table to be somehow related to the Section and Content tables so I can avoid manually adding constraints, or triggers that delete the relationships when a Section or Content is deleted...

Thanks as usual for the input! <3

+1  A: 

Yes, there is a much cleaner way to do this:

  • one table tracks the relations from Section to Section and enforces them as foreign key constraints
  • one table tracks the relations from Section to Content and enforces them as foreign key constraints
  • one table tracks the relations from Content to Section and enforces them as foreign key constraints
  • one table tracks the relations from Content to Content and enforces them as foreign key constraints

This is much cleaner than a single table with overloaded IDs that cannot be enforced by foreign key constraints. The fact that the data modeling, nor the domain modeling patterns, never mention a pattern like the one you describe should be your first alarm bell. The second alarm should be that the engine cannot enforce the constraints you envision and you have to dwell into triggers.

Having four distinct relationships modeled in one table brings no elegance to the model, it only adds obfuscation. Relational model is not C++: it has no inheritance, it has no polymorphism, it has no overloading. Trying to enforce a OO mind set into data modeling has led many a fine developers into a mud of unmaintainable trigger mesh of on-disk table-like bits vaguely resembling 'data'.

Remus Rusanu
you are absolutely right, but the problem I have with this is that if I ever (god forbid) want to add a third table with a third type of relational data (say "News", that also relate to the general sections and content in the site, for example), I would have to add 5 extra tables... 9 tables to relate three types of data seems like a lot, and it is exponentially so. thanks for the input though!
Nico
Is every table you add to your model going to be related to every other existing table? The fact that you have 4 many-to-many relations between just two tables is already a worrying sign, and if adding a new entity type (a new table) increases the number of relations exponentially (irrelevant of whether those relations are enforced in one table or two or 9) makes me seriously doubt the validity of your data model design.
Remus Rusanu
For instance, consider that if what you model is really 2 (or 3 or more) types of Snippets (Content, Section and now News) then you really have one single many-to-many relation table between Snippets and Snippets, and the relations between Snippets and Content/Section/News can be modeled as one of the table-inheritance patterns, eg.http://martinfowler.com/eaaCatalog/classTableInheritance.html
Remus Rusanu
+2  A: 

Here's how I would design it:

CREATE TABLE Pairables (
  PairableID INT IDENTITY PRIMARY KEY,
  ...other columns common to both Section and Content...
);

CREATE TABLE Sections (
  SectionID INT PRIMARY KEY,
  ...other columns specific to sections...
  FOREIGN KEY (SectionID) REFERENCES Pairables(PairableID)
);

CREATE TABLE Contents (
  ContentID INT PRIMARY KEY,
  ...other columns specific to contents...
  FOREIGN KEY (ContentID) REFERENCES Pairables(PairableID)
);

CREATE TABLE Pairs (
  PairID     INT NOT NULL,
  PairableId INT NOT NULL,
  IsSource   BIT NOT NULL,
  PRIMARY KEY (PairID, PairableID),
  FOREIGN KEY (PairableID) REFERENCES Pairables(PairableID)
);

You would insert two rows in Pairs for each pair.

Now it's easy to search for either type of pairable entity, you can search for either source or target in the same column, and you still only need one many-to-many intersection table.

Bill Karwin
This is more or less what I would have suggested, except that I think there is an omission. Shouldn't the Pairs table have two different FK columns each mapped to PairableId's?
RMorrisey
@RMorrisey: Perhaps, because one is source and the other is target. I use the pattern above when pairs are reciprocal "friends" with no proscribed order.
Bill Karwin