views:

327

answers:

10

I am working on a couple of link tables and I got to thinking (Danger Will Robinson, Danger) what are the possible structures of a link table and what are their pro's and con's.

I came up with a few possible strictures for the link table:

Traditional 3 column model

  • id - auto-numbered PRIMARY
  • table1fk - foreign key
  • table2fk - foreign key

It's a classic, in most of the books, 'nuff said.

Indexed 3 column model

  • id - auto-numbered PRIMARY
  • table1fk - foreign key INDEX ('table1fk')
  • table2fk - foreign key INDEX ('table2fk')

In my own experience, the fields that you are querying against are not indexed in the traditional model. I have found that indexing the foreign key fields does improve performance as would be expected. Not a major change but a nice optimizing tweak.

Composite key 2 columns ADD PRIMARY KEY ('table1fk' , 'table2fk')

  • table1fk - foreign key
  • table2fk - foreign key

With this I use a composite key so that a record from table1 can only be linked to a record on table2 once. Because the key is composite I can add records (1,1), (1,2), (2,2) without any duplication errors.

Any potential problems with the composite key 2 columns option? Is there an indexing issue that this might cause? A performance hit? Anything that would disqualify this as a possible option?

A: 

i've used both, the only benefit of using the first model (with uid) is that you can transport the identifier around as a number, whereas in some cases you would have to do some string concatenation with the composite key to transport it around.

i agree that not indexing the foreign keys is a bad idea whichever way you go.

pstanton
Transport? How so? I am assuming moving from db to db, or db to text. I am unfamiliar with the term.
tvanover
I believe he means passing the entity around in your code, or in a querystring, etc. You could always have 2 parameters in whatever method/function you are calling, but sometimes it's easier to have 1 that represents the record as opposed to both.
Matt Dawdy
yes, i mean what matt says.
pstanton
A: 

If you are using an ORM to get to/alter the data, some of them require a single-column primary key (Thank you Tom H for pointing this out) in order to function correctly (I believe Subsonic 2.x was this way, not sure about 3.x).

In my mind, having the primary key doesn't impact performance to any measurable degree, so I usually use it.

Matt Dawdy
By "primary key" I'm guessing that you mean, "single-column primary key". That's a pretty important distinction. A composite primary key is also a primary key.
Tom H.
Doh! That's exactly what I meant. Thanks for the clarification. I'll edit the answer to show that, too.
Matt Dawdy
+9  A: 

I would use composite key, and no extra meaningless key.

I would not use a ORM system that enforces such rules on my db structure.

Brimstedt
besides, I try to avoid IDENTITY/AUTO_INCREMENT fields, as they tend to make work with tables harder, and gives you less control.But it's beside the point in this case, and they DO have their purpose..
Brimstedt
I woudln't choose to use an ORM that enforces that, either, but in some jobs (like mine) you don't get to choose sometimes. I work on close to 40 projects a year, and not all of them start from scratch but are enhancements on existing client sites where an ORM is already in place.
Matt Dawdy
+1 Good heads up on the ORM. Will watch out for that.
tvanover
+2  A: 

If this is a true many-to-many join table, then dump unecessary id column (unless your ORM requires one. in that case you've got to decide whether your intellect is going to trump your practicality).

But I find that true join tables are pretty rare. It usually isn't long before I start wanting to put some other data in that table. Because of that I almost always model these join tables as entities from the beginning and stick an id in there.

Mike
+4  A: 

For true link tables, they typically do not exist as object entities in my object models. Thus the surrogate key is not ever used. The removable of an item from a collection results in a removal of an item from a link relationship where both foreign keys are known (Person.Siblings.Remove(Sibling) or Person.RemoveSibling(Sibling) which is appropriately translated at the data access layer as usp_Person_RemoveSibling(PersonID, SiblingID)).

As Mike mentioned, if it does become an actual entity in your object model, then it may merit an ID. However, even with addition of temporal factors like effective start and end dates of the relationship and things like that, it's not always clear. For instance, the collection may have an effective date associated at the aggregate level, so the relationship itself may still not become an entity with any exposed properties.

I'd like to add that you might very well need the table indexed both ways on the two foreign key columns.

Cade Roux
A: 

I (almost) always use the additional single-column primary key. This generally makes it easier to build user interfaces, because when a user selects that particular linking entity I can identify with a single integer value rather than having to create and then parse compound identifiers.

Larry Lustig
+1  A: 

Composite PK and turn off clustering.

Josh Einstein
could you elaborate on the clustering? why turn it off?
Brimstedt
When you create a primary key by default it is created as a clustered index. That's fine if your primary keys will be primarily inserted or sought in sorted order. But for a link table like this the relationships will most likely not be in any meaningful order thus it becomes more work for SQL to maintain the table.
Josh Einstein
+1  A: 

If you need to traverse the join table 'in both directions', that is starting with a table1fk or a table2fk key only, you might consider adding a second, reversed, composite index.

ADD KEY ('table2fk', 'table1fk')
martin clayton
+1  A: 

The correct answer is:

  • Primary key is ('table1fk' , 'table2fk')
  • Another index on ('table2fk' , 'table1fk')

Because:

  • You don't need an index on table1fk or table2fk alone: the optimiser will use the PK
  • You'll most likely use the table "both" ways
  • Adding a surrogate key is only needed because of braindead ORMs
gbn
Really? I can understand the first point, but I am surprised that joining table1->link->table2 will perform differently than table2->link->table1
tvanover
@tvanover: It gives the optimiser more options + don't forget: you'll have a WHERE or EXISTS or may not touch table 2 at all if do "WHERE link.table2fk = @table2keyvalue"
gbn
A: 

There is something called identifying and non-identifying relationship. With identifying relationships the FK is a part of the PK in the many-to-many table. For example, say we have tables Person, Company and a many-to-many table Employment. In an identifying relationship both fk PersonID and CompanyID are part of the pk, so we can not repeat PersonID, CompanyID combination.

TABLE Employment(PersonID int (PK,FK), CompanyID int (PK,FK))

Now, suppose we want to capture history of employment, so a person can leave a company, work somewhere else and return to the same company later. The relationship is non-identifying here, combination of PersonID, CompanyID can now repeat, so the table would look something like:

TABLE Employment(EmploymentID int (PK), PersonID int (FK), CompanyID int (FK), 
                     FromDate datetime, ToDate datetime)
Damir Sudarevic