Rails doing another select with present? - ruby-on-rails-3

My action populates an instance variable #websites - which it gets from the database.
#websites = Website.all
I can spin through the #websites and see there is only one call to the database. Happy Days.
<% #websites.each do |website| %>
...
<% end %>
This results in:
Website Load (0.1ms) SELECT "websites".* FROM "websites"
But if I wrap it with a present? like so:
<% if #websites.present? %>
<% #websites.each do |website| %>
<% end %>
<% else %>
Now I get two database calls
(0.2ms) SELECT COUNT(*) FROM "websites"
Website Load (0.1ms) SELECT "websites".* FROM "websites"
Why are there two database calls being made? And how do I stop the second call being made.
To me the #websites variable has all of the websites in memory. Surely rails would just count the items in memory rather than doing a new SELECT on the database?

This method call,
#websites = Website.all
does not load the records immediately into memory. Its just an active record relation. The first operation that you do with this relation is calling the present? and to satisfy this request, Rails need not load all the records into memory. So ActiveRelation just optimizes the relation and does a count query to get you the results. Now only when you access the #websites using the each method the records are loaded.
If you want to avoid that extra query, you can convert the Relation to an array but doing,
#websites = Website.all.to_a
This loads the records and renders an Array instead of ActiveRelation. But the catch is Active relation cannot chain further queries with this relation. But I am assuming that isnt necessary in your situation.

Related

optimize sql query rails

On posts index page I list all posts this way:
posts_controller.rb
def index
#posts = Post.includes(:comments).paginate(:page => params[:page]).order("created_at DESC")
end
index.html.erb
<%= render #posts %>
_post.html.erb
<%= gravatar_for post.user, size:20 %>
<%= link_to "#{post.title}", post_path(post) %>
<%= time_ago_in_words(post.created_at) %>
<%= post.comments.count %>
<%= post.category.name if post.category %>
35 posts per page
When I first load the page in dev env,
rack-mini-profiler shows this time: 1441.1 ms
after a few reloads: ~700 ms
Can I somehow decrease this time and number of sql requests?
Here're rmp images if it helps:
You could decrease the number of sql queries by:
including user as well as comments, since you seem to be using that when displaying the gravatar
changing post.comments.count to post.comments.size
While size, count, length are synonymous for arrays, for active record relations or associations they are not the same:
length loads the association (unless it is already loaded) and returns the length of the array
count does a select count(*) query whether the association is loaded or not
size uses length if the association is loaded and count if not.
In your case the comments association is loaded, but because you are using count, it's not actually used
Further, you don't actually seem to be using the comments collection for anything other than printing the number of records. If that's indeed the case, use counter_cache (4.1.2.3) instead of querying for the comments (the number of comments will be available in the parent record Post).
Also consider a client side alternative to time_ago_in_words. It will also help if you later decide to cache the entire section/page.
And finally retrieve only the fields you're going to use. In this case, I can imagine the Post contains a large amount of text for the content and it's not used anywhere (but still needs to be transmitted from the DB).
Adding an index on the reference column (comments in your case) might help.
add_index :posts, :comment_id

Null vs Nil in database

I am using an SQLite3 database with my rails app and am having a very frustrating time trying to solve this problem.
I want to populate check boxes in a different manner depending on if the form (_form.html.erb) is being rendered in update or in create so:
<% if #foo.new_record? %>
<% query = "association_id == nil" %>
<% else %>
<% query = "" %>
<% end %>
and then I pass the query string to my check boxes
collection_check_boxes(:host, :association_ids, Association.where(query), :id, :name)
What this should do is give me all of the associations as check boxes for update and only ones that don't already have an association_id when creating.
If I replace the query with :association_id=>nil I will get the desired result for create.
If I replace the query with Association.all, I get the result for update.
I could just replicate all of my code manually in the check for new_record? but my code is already rather long and complicated and that would make it doubly so. So I wanted to just pass a string (query) to my where call. This is where I ran into trouble.
When I look in the rails console and make direct SQL queries I notice that returned files without an association_id will show up as association_id: nil. So I try to match to nil but that gives me an "unknown column: nil" error from SQL. When I try to match to the SQL NULL it doesn't find anything.
How is a nil attribute stored in the database and how do I query to find it using a string?
You should do
<% query = "association_id is null" %>
Association.where(:association_id => nil)
will be converted internally into a query like below
select * from associations where association_id is null

ActiveRecord Joins

Ok, so, if I do a User.joins(:session_users), I only get the attributes of users table.
How do I get the attributes of both tables in ActiveRecord way, i.e., not SQL?
EDIT ONE
Ok, based on the first answer, I'm trying to have it displayed.
So, this is the method written in Users Controller
def blah
#users = User.includes(:session_users)
#users.each do |user|
user.session_users
end
end
Then I have this in the users view blah.html.erb
<%= #users.session_users %>
And this in the routing section:
match "/users/blah" => "users#blah"
I think you want includes instead of joins. See http://railscasts.com/episodes/181-include-vs-joins for more info. This should fetch columns for both,
users = User.includes(:session_users)
users.each do |user|
user.session_users
end
Note, this still performs 2 SQL queries.
Edit
Updated answer assumes that a user has_many :session_users
Routes:
# config/routes.rb
get '/users/blah' => 'users#blah'
Controller:
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def blah
#users = User.includes(:session_users)
end
end
View:
# app/views/users/blah.html.erb
<% #users.each do |user| %>
<%= user.name %> // assumes user has a name attribute
<% user.session_users.each do |session_user| %>
<%= session_user.attributes %> // prints all the attributes
<%= session_user.created_at %> // assumes the user has a created_at attribute
<% end %>
<% end %>
If you really need to add fields from a joined table to the yielded objects, you can add select:
User.joins("INNER JOIN stolen_passwords
ON users.social_security_number=stolen_passwords.ssn")
.select("*").find_each do |user|
logger.debug {
"A #{user.full_name} has a password hash #{user.password_hash}"
}
end
Here imaginary user.full_name is an instance method of User and user.password_hash comes from the stolen_passwords table. You could also limit the queried/returned fields by listing them in the call to select explicitly.
We sometimes use this in rake tasks that enrich the database from or compare it against third party data sources: we would join our tables with provided tables and generate a 'side-by-side' CSV with columns from both. Plain SQL would work just as well, but going via active record often allows to reuse familiar scopes, or methods that perform calculations in ruby.
Caveat
Unfortunately the fields coming from the joined tables will not be cast to appropriate ruby types, they will all be strings (which is especially logical if using SQL string for the joins). But it is easy to cast the joined attributes with something like:
module CastJoinedColumns
def cast_joined_columns joined_record
columns_hash.each do |column_name, column|
if joined_record.attributes.include?(column_name)
joined_record[column_name] = column.type_cast(joined_record[column_name])
end
end
end
end
This module is meant to be extended into a model appearing on the right side of the join and the method be called with a joined record. It might misbehave and should be improved for the cases where the same column name appears in multiple tables, but is an ok starting point (works perfectly for us with third party data sources using column names guaranteed not to clash with our own).

Limit not working as expected in Rails3

I have the following code in my practices controller index method:
#practices = #activity.practices.order('created_at DESC').limit(20)
Unfortunately, the limit call is not working, and #practices is returning >20 objects. I'm sure this is a simple thing to fix, but I'm not sure what I'm doing wrong. I seem to be using this method in the way prescribed in the docs..
I have tried placing the call to limit before the order call, with the same result.
UPDATE
SQL LOG:
Practice Load (1.6ms) SELECT "practices".* FROM "practices" WHERE ("practices".activity_id = 9) ORDER BY practiced_on DESC, created_at DESC LIMIT 20
However, further down the log, I found this:
Rendered practices/_practice.html.erb (216.9ms)
Activity Load (0.6ms) SELECT "activities".* FROM "activities" WHERE ("activities".user_id = 1)
Practice Load (0.8ms) SELECT "practices".* FROM "practices" WHERE ("practices".activity_id = 8) ORDER BY practiced_on DESC
CACHE (0.0ms) SELECT "practices".* FROM "practices" WHERE ("practices".activity_id = 9) ORDER BY practiced_on DESC
Which leads me to think that the partial is not accepting the correct collection. The partial is called thus:
<%= render #activity.practices %>
Any advice?
TIA
It looks like you are returning your limited collection correctly and storing it in the #practices instance variable.
However, you are rendering your partial with #activity.practices, which will trigger a lazy load of the entire unlimited practices collection, because that collection has not yet been loaded on the #activity object.
The answer
<%= render #practices %> should work.
Bonus round
Finally, if you want something a little more 'object-oriented' you could put your 'limited' query into a scope called 'recent_practices' so you could call:
<%= render :partial => 'practices', :object => #activity.recent_practices %>
The above example would use a local variable called 'recent_practices' inside the partial, rather than an instance variable.
More about ActiveRecord scopes here.

Running a SQL query in rails

I have a rails application where I have to subtract 2 time stamps and display the result on the webpage
I have used the following sql statement and I am getting the results.
select (julianday(resolutions.created_at)-julianday(tickets.created_at))*24 from tickets,resolutions
the value of tickets.created_at is 2010-04-05 18:59:02 which is a time stamp and value of resolutions.created_at is 2010-04-08 08:10:33
Now where do I put this sql so that I can be seen in my webpage.
I tried it in the views page with the following but nothing showed up:
<% #sql = "select (julianday(resolutions.created_at)-julianday(tickets.created_at))*24 from tickets,resolutions" %>
<% #t=(ActiveRecord::Base.connection.execute(#sql)) %>
So how do I display it on my webpage?
when I perform the above I get the output printed on the webpage as
061.1919444389641(julianday(resolutions.created_at)-julianday(tickets.created_at))*2461.1919444389641
only 61.1919444389641 is supposed to be printed but the query statement is also getting printed.
You should put your custom SQL in your model class, definitely not in the view.
In your controller
#tickets = Tickets.find(
:all,
:joins => :resolution,
:select => '(julianday(resolutions.created_at)-julianday(tickets.created_at))*24 AS interval'
)
In your view
<% #tickets.each do |ticket|%>
<%= ticket.interval %>
<% end %>
Generally you would put this logic in one of your models though, like this:
In your Tickets Model
def time_to_resolve
resolution.created_at - created_at
end
To reduce the number of queries when iterating over multiple queries you would use this:
Tickets.find(:all, :include => :resolution)