Spotlight on… Composite Keys

For those of us who have taken a course on database design in college or university, you may have run across a concept called ‘Composite Primary Keys’ (or sometimes ‘Compound Keys’ or ‘Concatenated Keys’, and abbreviated CPKs). It’s usually right before you tackle JOINs and right after you fight with the “surrogate key” or “primary key” concept.

Boiling CPKs down, they’re just a way of identifying a row by multiple keys rather than one. So instead of an auto_incrementing “serial” primary key (as in id), you’d have a combination of some_column and some_other_column that would uniquely identify a row.

CPKs aren’t as prevalent in the Rails world as Serial Keys (such as the auto-incrementing :id column), but if you’re going to support legacy, integration or reporting databases or just de-normalized schemas for performance reasons, they can be invaluable. So sure, Surrogate Keys are a great convenience, but sometimes they just aren’t an option.

Let’s briefly take a look at how a few ruby ORMs support Composite Primary Keys and then we’ll talk about DataMapper’s support for CPKs.

ActiveRecord

In short, ActiveRecord doesn’t support CPKs without the help of an external library. Dr. Nic Williams Composite Keys is an effort to overcome this limitation.

Sequel

Unlike ActiveRecord, Sequel supports CPKs natively:

1
2
3
4
5
6
class Post < Sequel::Model
  set_primary_key [ :category, :title ]
end

post = Post.get('ruby', 'hello world')
post.key  # => [ 'ruby', 'hello world' ]

p(attribution). example compiled from http://github.com/jeremyevans/sequel.

DataMapper

The latest DataMapper was designed from the ground up to support CPKs:

1
2
3
4
5
6
7
8
9
10
11
class Pig
  include DataMapper::Resource

  property :id,   Integer, :key => true
  property :slug, String,  :key => true
  property :name, String
end

pig = Pig.get(1, 'Porky')

pig.key  # => [ 1, 'Wilbur' ]

We declared our keys by adding the :key => true to the appropriate properties. The order is important as it will determine the order keys are addressed throughout the system.

Next, we mixed and matched the keys’ types. :id is a Integer, but :slug is a String. DataMapper didn’t flinch when we defined a key column as a String because it supports Natural Keys as well.

Lastly, when retrieving rows via get and [] with a CPK, we supplied the keys in the order they were defined within our model. For example, we defined :id first, then :slug second; later, we retrieved Porky by specifying his :id and :slug in the same order. Additionally, when we asked Wilbur for his keys, he handed us an array in the order the keys were defined.

We didn’t need to mix in an external library to get support for CPKs, nor did we need to call a set_primary_key method and then supply more than one key to it. DataMapper supports Composite Primary Keys intuitively and without compromise!

In later “Spotlight On…” articles, we’ll examine and demonstrate other DataMapper features or persistence concepts as well as compare similar features with other ORMs or libraries.

Contribute a “Spotlight On…” Article

Got something important to say? Want something explained a little better or demonstrated? Contribute or request a “Spotlight On…” article! Email the DataMapper Mailing List with the request or contribution and we’ll post it here.