OMFG! This is the moment ya all have been waiting for..ActiveRecord partial updates are now possible !! It’ll make your application run 100x faster!!
Background
ActiveRecord updates all the columns when you save the object, without bothering to see if the column was changed or not.
Notice the UPDATE statement in the following console session :
1 2 3 4 5 6 |
>> p = Person.find :first Person Load (0.002784) SELECT * FROM people LIMIT 1 => #<Person id: 1, name: "Pratik", address: "Shangri-la", history: "pff", created_at: "2007-12-18 05:07:53", updated_at: "2007-12-18 06:08:13"> >> p.save Person Update (0.001178) UPDATE people SET "created_at" = '2007-12-18 05:07:53', "name" = 'Pratik', "history" = 'pff', "address" = 'Shangri-la', "updated_at" = '2007-12-18 06:20:11' WHERE "id" = 1 => true |
O MAN !

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
module ActiveRecord module Changed def self.included(base) base.alias_method_chain :write_attribute, :changed base.alias_method_chain :update_without_timestamps, :changed base.alias_method_chain :save, :changed base.alias_method_chain :save!, :changed end private def write_attribute_with_changed(attr_name, value) # If you're accessing attr= method, you should change the value ;-) changed_attributes << attr_name.to_s write_attribute_without_changed(attr_name, value) end def update_without_timestamps_with_changed quoted_attributes = attributes_with_quotes(false, false) quoted_attributes.reject! { |key, value| !changed_attributes.include?(key.to_s)} return 0 if quoted_attributes.empty? connection.update( "UPDATE #{self.class.quoted_table_name} " + "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " + "WHERE #{connection.quote_column_name(self.class.primary_key)} " + "= #{quote_value(id)}", "#{self.class.name} Update" ) end def changed_attributes @changed_attributes ||= Set.new end def save_with_changed save_without_changed ensure changed_attributes.clear end def save_with_changed! save_without_changed! ensure changed_attributes.clear end end end ActiveRecord::Base.send :include, ActiveRecord::Changed |
Ok, you won’t too much of performance boost with this. I lied. It was a joke. Get over it.
Using this code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
>> p = Person.find :first Person Load (0.000538) SELECT * FROM people LIMIT 1 => #<Person id: 1, name: "Hellll", address: "whatever", history: "pff", created_at: "2007-12-18 05:07:53", updated_at: "2007-12-18 05:40:45"> >> p.save Person Update (0.000536) UPDATE people SET "updated_at" = '2007-12-18 06:07:55' WHERE "id" = 1 => true >> p.name = "Pratik" => "Pratik" >> p.save Person Update (0.000908) UPDATE people SET "name" = 'Pratik', "updated_at" = '2007-12-18 06:08:04' WHERE "id" = 1 => true >> p.address = "Shangri-la" => "Shangri-la" >> p.save Person Update (0.000542) UPDATE people SET "address" = 'Shangri-la', "updated_at" = '2007-12-18 06:08:13' WHERE "id" = 1 => true |
But..
Yes, there is a big fat but here
Do partial updates make sense ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Whatever < <Something>::Base def validate errors.add("Invalid") if self.foo == "Hello" and self.bar == "World" end end Current Record State : { :foo => "abc", :bar => "xyz" } ( Two processes fetch the same record concurrently ) t0 : Process 1 : Fetch the record | Process 2 : Fetch the record t1 : Process 1 : set 'foo' to "Hello". keep 'bar' as "xyz" { :foo => "Hello", :bar => "xyz" } Partial update will execute the query only to update :foo t2 : Process 2 : set 'bar' to "World". keep 'bar' as "abc" { :foo => "abc", :bar => "World" } Partial update will execute the query only to update :bar t3 : Final state of the record : { :foo => "Hello", :bar => "World" } |
Yes, it can leave your records in invalid state
Awww…Did I make you sad :-( ?

Well, don’t worry. The point is :
- It is very simple to do partial updates with ActiveRecord. It’s no rocket science.
- Know what you’re doing. Make sure you don’t screw up validations. Use optimistic locking. ( Thanks to Lawrence for pointing it out )
- Probably go for more declarative style if you really have a situation where partial updates will make a difference. Something like :
1 2 3 |
class Whatever < <Something>::Base lazy_attributes :some_column_which_is_huge_and_rarely_changes end |
And apply the partial update logic only to the attributes supplied to lazy_attributes





