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





It’s about time it did partial updates. ;)
No invalid state possible:
t.integer :lock_version, :null => false, :default => 0
Err…Thanks Lawrence. I completely missed Optimistic locking.
OMFG, 100x faster?? :))
Nice post, Pratik. It’s about time someone did dirty field checking. Let’s make this a plugin!
This is pretty damn awesome. Looks like material for 2.1 imo. Not only do you get phat and efficient DB inserts, you also get an awesome way of figuring out which attributes that changed on an AR instance, which is useful outside of this particular context, too. Which again makes it very suitable material for a core patch, imo, not “just” a plugin.
Mislav : I am playing with an idea of a plugin called “railsex” without the focus on “sex” part ;-) It’s supposed to be “rails extensions” for all the cool stuff which cannot be in the core for whatever reasons. So, do join in when I get it rolling! Chu Yeow is interested too. I think it might be a good idea.
August : I agree that having something that having a good way to figure out dirty attributes is a good thing to have in the core and dirty does a very good job. But I’m not quite sure about partial updates thing. I guess on-demand partial updates might be a good fit. I’ll do the plugin first I think.
I agree this is good news for Rails. Pratik, have you seen the G framework?
Is this still working in edge Rails?
I tried using it, and it doesn’t seem to have any affect (the logs suggest all fields are still being written). It also causes an exception to be thrown when using update_attribute().