I have a table with id|patient_id|client_id|active. A record is unique by patient_id, client_id meaning there should only be one enrollment per patient per client. Normally I would make that the primary key, but in rails I have id as my primary key.
What is the best way to enforce this? Validations?
Composite keys in SQL prove to be useful in those cases where you have a requirement of keys that can uniquely identify records for better search purposes, but you do not possess any single unique column. In such cases, you must combine multiple columns to create a unique key.
In database design, a composite key is a candidate key that consists of two or more attributes (table columns) that together uniquely identify an entity occurrence (table row). A compound key is a composite key for which each attribute that makes up the key is a foreign key in its own right.
A composite key is made by the combination of two or more columns in a table that can be used to uniquely identify each row in the table when the columns are combined uniqueness of a row is guaranteed, but when it is taken individually it does not guarantee uniqueness, or it can also be understood as a primary key made ...
Sounds like you have a model relationship of:
class Client < ActiveRecord::Base
has_many :patients, :through => :enrollments
has_many :enrollments
end
class ClientPatient < ActiveRecord::Base
belongs_to :client
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :clients, :through => :enrollments
has_many :enrollments
end
To enforce your constraint I would do it in ActiveRecord, so that you get proper feedback when attempting to save a record that breaks the constraint. I would just modify your ClientPatient model like so:
class Enrollment < ActiveRecord::Base
belongs_to :client
belongs_to :patient
validates_uniqueness_of :patient_id, :scope => :client_id
end
Be careful though because, while this is great for small-scale applications it is still prone to possible race conditions as described here: http://apidock.com/rails/v3.0.5/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of under "Concurrency and Integrity"
As they describe there, you should also add a unique index to the table in the database. This will provide two immediate benefits:
In a migration file add the following:
add_index :enrollments, [:patient_id, :client_id], :unique => true
Hopefully this was helpful :)
Edit (fixed some naming issues and a couple obvious bugs):
It's then very easy to find the data you're looking for:
Client.find_by_name("Bob Smith").patients
Patient.find_by_name("Henry Person").clients
Validations would work (Back them up with a unique index!), but there's no way to get a true composite primary key in vanilla Rails. If you want a real composite primary key, you're going to need a gem/plugin - composite_primary_keys is the one I found, but I'm sure there are others.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With