27.02.2013 Views

Rails%203%20In%20Action

Rails%203%20In%20Action

Rails%203%20In%20Action

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Database query enhancements<br />

all on the same page, but also along with these tickets you wanted to display all the<br />

tags for these tickets. Before you render this page, you know all the tickets but don’t<br />

yet know what the tags are for the tickets. Therefore, you’d need to retrieve the tags as<br />

you are iterating over each of the tickets, generating another query to retrieve all the<br />

tags for each ticket.<br />

This is the N+1 selects problem. You have an initial query for all of your tickets,<br />

but then N queries more, depending on the amount of tickets you’re showing. This<br />

problem is not so much of a big deal now that you’ve got pagination, but it still can<br />

crop up.<br />

16.2.1 Eager loading<br />

In your app/views/projects/show.html.erb you can perform N+1 selects, asking for<br />

each ticket’s tags just like in the example, by putting this line within the block where<br />

you iterate over each ticket:<br />

<br />

When you start your server using rails server and navigate to your first project’s<br />

page, Rails will diligently run through each ticket in the @tickets array, performing a<br />

query for each one to find its tags. If you switch back over to the console, you’ll see<br />

queries like this:<br />

SELECT * FROM "tags"<br />

INNER JOIN "tags_tickets" ON "tags".id = "tags_tickets".tag_id<br />

WHERE ("tags_tickets".ticket_id = 1 )<br />

There should be 50 of these little queries, and 50 adds up to a big number 5 when it<br />

comes to lots of requests hitting this page and running these queries. Fifty requests to<br />

this page would result in over 2,500 queries. Oh, your poor database server! 6 It would<br />

be much better if you didn’t have to run so many queries.<br />

Thankfully, there’s yet another thing in Rails that helps us be better programmers<br />

and better friends with our databases. This wonderful invention is known as eager loading<br />

and will allow you to run two queries to get all the tickets and all the tags, rather<br />

than one query for the ticket and N queries for all the tags for all the tickets.<br />

There are two ways of doing this: you can use the joins or includes method when<br />

you attempt to grab all the tags for the tickets in app/controllers/projects<br />

_controller.rb. You’re currently grabbing and paginating all the tickets for the current<br />

project using this line in the show action in ProjectsController:<br />

@tickets = @project.tickets.page(params[:page])<br />

The @project.tickets part of this line generates a query, 7 but doesn’t eager-load the<br />

tags yet. To make it do this, you could use the joins method like this:<br />

@tickets = @project.tickets.joins(:tags).page(params[:page])<br />

5 When used in a function that uses squares, or even worse, cubes.<br />

6 Yes, they’re made for this kind of thing, but that’s not the point!<br />

7 But doesn’t run it! When it gets to the view and you call each on it, then it runs.<br />

445

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!