Retrieving Multiple Objects in Batches
find_each
User.find_each(:batch_size => 5000) do |user|
NewsLetter.weekly_deliver(user)
end
User.find_each(:batch_size => 5000, :start => 2000) do |user|
NewsLetter.weekly_deliver(user)
end
:start option allows you to configure the first ID of the sequence if the lowest is not the one you need.
find_each accepts the same options as the regularfind method. However, :order and :limit are needed internally and hence not allowed to be passed explicitly.
Conditions
Client.where("orders_count = ?", params[:orders])
instead of
Client.where("orders_count = #{params[:orders]}")
because of SQL injection
Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date))
Hash Conditions
Subset Conditions
Client.where(:orders_count => [1,3,5])
Ordering
Client.order("orders_count ASC, created_at DESC")
Selecting Specific Fields
Client.select("viewable_by, locked")
Be careful because this also means you’re initializing a model object with only the fields that you’ve selected. If you attempt to access a field that is not in the initialized record you’ll receive:
ActiveRecord::MissingAttributeError: missing attribute: <attribute>
Client.select("DISTINCT(name)")
Pessimistic Locking
Item.transaction do
i = Item.lock.first
i.name = 'Jones'
i.save
end
Item.transaction
do
i = Item.lock(
"LOCK IN SHARE MODE"
).find(
1
)
i.increment!(
:views
)
end
Joining Tables
Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
Joining a Single Association
Category.joins(:posts)
SELECT categories.* FROM categories
INNER JOIN posts ON posts.category_id = categories.id
Joining Multiple Associations
Post.joins(:category, :comments)
SELECT posts.* FROM posts
INNER JOIN categories ON posts.category_id = categories.id
INNER JOIN comments ON comments.post_id = posts.id
Joining Nested Associations (Single Level)
Post.joins(:comments => :guest)
Joining Nested Associations (Multiple Level)
Category.joins(:posts => [{:comments => :guest}, :tags])
Specifying Conditions on the Joined Tables
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where('orders.created_at' => time_range)
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where(:orders => {:created_at => time_range})
Eager Loading Associations
Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the includes method of the Model.find call. With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
Eager Loading Multiple Associations
Post.includes(:category, :comments)
Category.includes(:posts => [{:comments => :guest}, :tags]).find(1)
Dynamic Finders
For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called first_name on your Client model for example, you getfind_by_first_name and find_all_by_first_name for free from Active Record. If you have also have a locked field on the Client model, you also get find_by_locked and find_all_by_locked
If you want to find both by name and locked, you can chain these finders together by simply typingand between the fields for example Client.find_by_first_name_and_locked("Ryan", true)
There’s another set of dynamic finders that let you find or create/initialize objects if they aren’t found.
Finding by SQL
Client.find_by_sql("SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER clients.created_at desc")
find_by_sql has a close relative called connection#select_all. select_all will retrieve objects from the database using custom SQL just like find_by_sql but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
Existence of Objects
Client.exists?(1)
Client.exists?(1,2,3)
# or
Client.exists?([1,2,3])
Client.where(:first_name => 'Ryan').exists?
Client.exists?
Calculations
If you want to be more specific and find all the clients with their age present in the database you can use Client.count(:age).
Client.average("orders_count")
minimum maximun sum