-
Get a monthly update on best practices for delivering successful software.
Have you often run into a situation where you have a hash full of properties and you want to either create a new ActiveRecord object (if it doesn't exist) or update one (if it exists)?
For example,
class User
# has properties: ssn, first_name, :last_name, :age, :email, :password
def create_or_update_by_ssn(params)
user = Disease.find_by_ssn(params[:ssn])
if user.nil?
user = User.create(params)
else
user.update_attributes!(params)
end
end
end
Here, the business rule states that a user's SSN is unique, so called natural-key.
If we can identify what constitutes a natural-key for each model object in our domain-model, when we could DRY out this functionality.
With such a solution, the above code becomes:
class User natural_key :ssn end
And then the client call becomes:
User.create_or_update_by_natural_key(params)
If you think, such a thing could be useful, you can download the gem here.
Related posts:
Topics: activerecord, natural key, primary key, rails, ruby, surrogate key
Why not use AR-Extensions?
In my case I’m working with over 20,000 rows for my ‘import’ so running 40k queries (as your method would require) rather than 20k would be too costly.
Comment by Andrew Herron, Thursday, January 8, 2009 @ 4:29 pm
Andrew, Thanks for pointing me to ar-extensions. I was unaware of it. It seems Model.import with
n_duplicate_key_update is what could be used. I wonder if it is database agnostic. Thanks.
Comment by Sharad Jain, Thursday, January 8, 2009 @ 5:08 pm
I dug into the code for the plugin and it appears to take advantage of the MySQL syntax for insert.
What other database systems were you looking to use?
A separate version for SQLite could be written using the ON CONFLICT clause:
http://www.sqlite.org/lang_conflict.html
It doesn’t look so easy with Postgres and you might need a trigger for that. While needing a second query, it would at least be run in the DB and not require a new call through the Rails DB layer.
There are ways of creating a single query that would include conditionals to also do this though I’m not sure how complex or taxing it would be on some systems. Here’s some discussion on how to do it with Oracle:
http://www.sqlrecipes.com/sql_questions_answers/insert_update_record_if_already_exists-6/
Comment by Andrew Herron, Friday, January 9, 2009 @ 10:16 am