Why is my Ruby On Rails database query so slow? - ruby-on-rails

The following code always uses more then ten seconds. I have upgraded the server, but it doesn't help. I know I have some database design problems, but I can't modify that.
I am showing all the prices from differents locations of products of a category also in different time range, because the prices change every 15 days in each location.
controller
def prods_x_cat
# This will load all the products of a category
#products = Product.prods_x_cat(params[:id],:include => :raw_datas)
#cortes = RawData.cortes
respond_to do |format|
format.js { render :layout=>false}
end
end
prods_x_cat.js.erb
var jqxhr1 = $.ajax($("#loading_overlay .loading_message, #loading_overlay").fadeIn());
$('#datos').html("<%= escape_javascript render :partial=>'/shared/prods_x_cat'%>")
view
<%#cortes.each do |c|%>
<%=c.corte%>
<%end%>
<%#cortes.each do |c|%>
<%#fecha = c.corte_real%>
<div id="<%=c.corte%>" class="block no_padding">
<table class="display datatable">
<thead>
<tr>
<th>SKU</th>
<%Company.active.order('table_field ASC').each do |c|%>
<th><%=c.abbr%></th>
<%end%>
<th>Average</th>
<th>Mode</th>
<th>Minimum</th>
<th>Maximum</th>
</tr>
</thead>
<tbody>
<%#products.each do |p|%>
<tr class="gradeA">
<td><%=p.name%></td>
<%p.raw_datas.where("product_id='#{p.id}' and corte_real='#{#fecha}'").each do |prd|%>
<td><%=prd.location1.to_f.round(2)%></td>
<td><%=prd.location2.to_f.round(2)%></td>
<td><%=prd.location3.to_f.round(2)%></td>
<td><%=prd.location4.to_f.round(2)%></td>
<td><%=prd.location5.to_f.round(2)%></td>
<td><%=prd.location6.to_f.round(2)%></td>
<td><%=prd.location7.to_f.round(2)%></td>
<td><%=prd.location8.to_f.round(2)%></td>
<td><%=prd.promedio.to_f.round(2)%></td>
<td><%=prd.moda%></td>
<td><%=prd.minimo.to_f.round(2)%></td>
<td><%=prd.maximo.to_f.round(2)%></td>
<%end%>
</tr>
<%end%>
</tbody>
</table>
</div>
<%end%>
</div>

This kind of question is pretty much impossible to answer without seeing all the code involved. Instead I can help you try to figure out where the problem is.
There are good tools for finding where your performance problems are (e.g. ruby-prof) but if you want a quick primitive way to find where your issue is, just use Time.now. For example, you could change your controller action to be:
def prods_x_cat
# This will load all the products of a category
a1 = Time.now
#products = Product.prods_x_cat(params[:id],:include => :raw_datas)
b1 = Time.now
p "Time for finding products: #{b1 - a1}"
a2 = Time.now
#cortes = RawData.cortes
b2 = Time.now
p "Time for finding cortes: #{b2 - a2}"
respond_to do |format|
format.js { render :layout=>false}
end
end
If the printouts suggest that the time is taken up elsewhere, start doing something similar in your template. Focus on the database calls.

Related

I want to make a helper and template to dynamically generate a view for a collection of active record objects

This may sound strange, but none the less I want to learn how to do it and I need some help getting there. I'm not sure how to approach this. I'm hoping to get some dev love on this.... Let me explain by giving an example. (Btw thank you---you are awesome!)
Instead of this in my view:
<table>
#users.map do |user|
...
</table>
I want to extract it away into a helper that I can reuse for other collections.
So I want to say instead:
#users.to_table({
template: "simple_template",
header: ["Full Name","Email"],
column: ["name", "email"]
})
So in my application_helper I have something like this: (pseudo-ish code)
class ActiveRecord::Relation
def to_table *args
load args.template
self.map do |j|
args.header do |header|
j.header
end
args.column do |column|
j.column
end
end
end
end
I have no idea how to wire this up. (helper or table template) Definitely an order of magnitude above my current skill level. Need some serious direction.. I'm asking this because I feel like I hit a learning plateau and need help busting through to something more challenging (hence this question)... Hope it's clear, if not ask for clarification. Thanks for reading... Thanks for helping! =)
Not guaranteeing this will work it is just to show the syntax issues:
class ActiveRecord::Relation
def to_table(options={})
load options[:template]
self.map do |j|
Hash[
args[:headers].zip(args[:columns].map{ |column| j.send(column) }
]
end
end
end
Not Sure about the load part I think this should be handled outside of the relation as it is a view issue and has nothing to do with the ActiveRecord::Relation but this method will return an Array of Hashes like
[{"Full Name" => "USER 1 Name", "Email" => "USER1#email.com},{"Full Name" => "USER 2 Name", "Email" => "USER2#email.com"}]
In your current method args which is an array now based on the * will not respond to things like template or column. Like I said I have never really tried to implement anything in this way but the syntax change might get you headed in the right direction. Also handling should be put in place for when template is not passed or headers.count != columns.count.
Best bet is probably something like this
<%= render "template", obj: #user.to_table(headers: ["Full Name","Email"],columns: ["name", "email"]) %>
in _template.rb
<table>
<thead>
<tr>
<% obj.first.keys.each do |header|
<th><%= header %></th>
<% end %>
</tr>
</thead>
<tbody>
<% obj.each do |row|
<tr>
<% row.values.each do |cell| %>
<td><%= cell %></td>
<% end %>
</tr>
<% end %>
</tbody>
Although if I had more time to think there are probably far simpler implementations of this maybe something like
<%= render 'template', locals:{collection: #users, headers: ["Full Name","Email"], columns: ["name", "Email"]} %>
UPDATE
I think making a view helper might be better in this instance like this
def make_table(collection,options={})
content_tag(:table,options[:table_options]) do
content_tag(:thead) do
content_tag(:tr) do
options[:headers].map do |header,header_options|
content_tag(:th,header,header_options,false)
end.join.html_safe
end
end
content_tag(:tbody,options[:body_options]) do
collection.map do |obj|
content_tag(:tr,options[:row_options]) do
options[:columns].map do |column,cell_options|
content_tag(:td,obj.public_send(column),cell_options,false)
end.join.html_safe
end
end.join.html_safe
end
end
end
call as
<%= make_table(#users,columns:{name:{class: "name"},email:{}},headers:{"Full Name"=>{class:"name_header"},"Email"=>{}}) %>
or without formatting
<%= make_table(#users,columns:[:name,:email],headers:["Full Name","Email"]) %>
This method requires an object collection and will accept the following through the options Hash
:table_options as a Hash to pass to the content tag for formatting the table
:headers as an Array or Hash (for formatting header rows)
:body_options as a Hash to pass to the content tag for formatting the table body
:row_options as a Hash to pass to the content tag for formatting the rows
:columns as an Array or Hash (for formatting the individual cells)
You can place this method in helpers/application_helper.rb and you will have access to it throughout the application. Although I have not fully vetted this method and it is currently more conceptual than anything else.

Average values by group

I have two tables in my web app: one is for Donors (called "donors") and the other is for Donation Amounts (called "donations). When you click on a donor, you can see all of their donations.
I'm trying to average values associated with a particular date, for a particular charity. For example, if these records exist for Donor A:
*Donor A*
Date Donation Amount
05/04/2013 30
05/04/2013 40
05/05/2013 15
05/05/2013 75
I'd like the system to also calculate and display that the average donation amount for 05/04/2013 was 35 and the average donation amount for 05/05/2013 was 45.
Currently I've tried using the group attribute in my donor model:
def self.average_donateperdate
Donation.average(:donateamount, conditions: ["donor_id = ?", #donor], group: "donatedate")
end
This doesn't seem to work. Since I'm new to Rails, I'm not entirely sure whether this is the right way to do it or not. There are a few other posts that touch on this topic, but none that have helped me solve the issue. Any help would be greatly appreciated!
The simplest syntax to do this is:
#donor.donations.group(:donatedate).average(:donateamount)
This will return a hash in the format { 'donatedate' => 'average donateamount' }.
Naturally, this assumes you have a has_many :donations association on your Donor model. A reusable method would look like:
def average_donation_by_date
donations.group(:donatedate).average(:donateamount)
end
And you can now simply call:
#donor.average_donation_by_date
To see this visually, you can call this in your controller:
#average_donations = #donor.average_donation_by_date.to_a.sort_by(&:first)
And your view might contain something like this:
<table>
<thead>
<tr>
<th>Date</th>
<th>Average Donation</th>
</tr>
</thead>
<tbody>
<% #average_donations.each do |date, amount| %>
<tr>
<td><%= date.strftime('MM/dd/yyyy') %></td>
<td><%= amount %></td>
</tr>
<% end %>
</tbody>
</table>
Reference
Rails api - calculate grouped values

Sorting my Rails Table

My controller:
def index
#unique_bug = Rating.find_by_sql("SELECT bug FROM ratings WHERE bug <> '' GROUP BY bug")
end
Rating Model:
def self.metrics_bug_count(bug)
count = where(bug:"#{bug}").count
total = Rating.find_by_sql("SELECT bug FROM ratings WHERE bug <> ''").count
percentage = (count.to_f/total.to_f * 100)
return count, percentage
end
My view:
<table class="table table-bordered">
<tr>
<th><h3>Bug</h3></th>
<th><h3>Total Occurrences</h3></th>
<th><h3>Frequency</h3></th>
</tr>
<tr>
<%#unique_bug.each do |rating| %>
<% count, percentage = Rating.metrics_bug_count(rating.bug)%>
<td><p class="text-center"><h4><%= rating.bug.capitalize %></h4></p></td>
<td><p class="text-center"><h4><%= count %></h4></p></td>
<td><p class="text-center"><h4><%= number_to_percentage(percentage, :precision => 2)%></h4></p></td>
</tr>
<%end%>
</table>
How can I make each row (Bug, Total, Frequency) to each be sortable? I watch the Railscast episode on sortable tables, however the sorting there is done in the controller. How can I sort my table with a more complex find that is being performed in my model.
you can use .order
For example, Rating.order("count DESC").
Besides, I think you can avoid using find_by_mysql and use some rails method to do your query which is more readable and easier.
You can get the unique 1 using .uniq as well.
try have a look at
http://guides.rubyonrails.org/active_record_querying.html

Ruby generate table from date range

I've been having a lot of trouble with this:
For an RPI application, all my results are within one table: results
I'm trying to print a table based on a specific data range: 2011-07-31..2012-07-01
In my controller:
class SeasonsController < ApplicationController
def s2012
#results = Result.all
#s2012 = Result.where(:date => (2011-07-31)..(2012-07-01))
end
end
In my view:
<table>
<tr>
<th>Event ID</th>
<th>Date</th>
</tr>
<% #s2012.each do |result| %>
<tr>
<td><%= result.event_id %></td>
<td><%= result.date %></td>
</tr>
<% end %>
</table>
This doesn't output any errors (small miracle), but nothing is displayed in the view. #results = Result.all prints all the whole table just fine. But how can I limit it to a specific data range?
I know you're trying to do this all ActiveRecordy, but imo it's actually less readable than if you just wrote the SQL where clause yourself.
Result.where('date BETWEEN ? AND ?',
Date.parse('2011-07-31'),
Date.parse('2012-07-01'))
maybe try:
Date.parse('2011-07-31')..Date.parse('2012-07-01')
In your current example rails will think you are doing arithmetic. You are looking at a date between the number 1973 and 2004. Instead you want to convert them to date objects first. Do
#s2012 = Result.where(:date => DateTime.parse('2011-07-31')..DateTime.parse('2012-07-01'))

Adding pagination to index.html.erb in Ruby on Rails - Easy Question

I am new to rails so go easy. I have built my first blog and would like to set it up such that in the <% post.each do |post| %> render of the posts, I would like it to work such that it only displays 10 posts and then has a link to view the next 10.
I am hoping this is an easy question. Let me know if you would like me to post any code.
You should use the gem will_paginate.
The installation and usage is really easy: https://github.com/mislav/will_paginate/wiki/installation
Example usage: http://github.com/mislav/will_paginate
will_paginate is definitely the way to go, I just thought I'd add a link to a railscasts will_paginate video showing you how to use it since sometimes that can be an easier way to learn the basics than reading documentation. Plus you'll learn a brief history on how pagination used to be done when it was an included part of Rails (it was slower and more cumbersome). The old classic pagination has been moved out into it's own plugin too, but it's only recommended if you were already using it when you upgraded Rails to a version where they took it out.
Railscasts has a ton of other great videos that you might find helpful. For example, once you get more advanced you might want to try ajax pagination
Check out the will_paginate Gem.
It’s very easy to do pagination on ActiveRecord models:
#posts = Post.paginate :page => params[:page], :order => 'created_at DESC'
In the view, page links can be rendered with a single view helper:
<%= will_paginate #posts %>
I've got problems using "will_paginate", installing the GEM then unistalling... a REAL PROBLEM! So I decide to program the pagination on RoR, it was not difficult so I wanted to share what I did:
Controller:
def index
#how many register per page i want to show
#b = 30
#params via GET from URL
a = params[:page].to_i
#first register per page
a1 = params[:page].to_i * #b
#the query can be easy...
#callcenters = Income.all(:joins => "LEFT JOIN radcheck ON radcheck.id = incomes.radcheck_id", :order => "created_at DESC",:limit => "#{a1},#{#b}", :select => "radcheck.username as clave,caller_id, created_at, value")
#I need to know how many pages somehow
#registrostotales = Income.all(:joins => "LEFT JOIN radcheck ON radcheck.id = incomes.radcheck_id", :select => "radcheck.username as clave,caller_id, created_at, value").count
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #callcenters }
end
end
View:
...
Total de registros: <%= numero = #registrostotales %><br/><br/>
<!-- total pages -->
<% pages = #registrostotales / #b %>
<!-- the last page -->
<% if pages % #b > 0 %><% pages = pages + 1 %><% end %>
Paginas:
<% (0..(pages-1)).each do |i| %>
<!-- href or link_to, http://xxxxxxxx/yyyy?page=i -->
<%= link_to i+1, :action => "index", :controller => "callcenters", :page => i %>
<% end %>
<!-- the view.. -->
<table>
<tr>
<th>#</th>
<th>Teléfono (ID)</th>
<th>Zona</th>
</tr>
<% #callcenters.each do |callcenter| %>
<tr>
<td><%= numero - params[:page].to_i * 30 %><% numero = numero - 1 %></td>
<td><%= callcenter.caller_id %></td>
<td><%= Node.first(:conditions => {:calling_id => callcenter.caller_id}).description %></td>
</tr>
<% end %>
</table>
I hope this helps to someone!
We can do this with ease by using 'will_paginate-bootstrap' gem.
To continue firstly you add a line to the gem file as,
gem 'will_paginate-bootstrap'.
Now run bundle install.
In the controller, you will be using like #post = Post.all to get the
contents from models.
Instead use this
#post = Post.paginate(:page=>params[:page],per_page:5)
Here in the above line per_page field is to specify how many records you need
in a page.
In index.html.erb
At the end of the code i.e before ending the body tag
Add this,
<%= will_paginate #post,renderer: BootstrapPagination::Rails %>
Note: I thought Post as modal and post as variable declared in the controller.
Just go with your variables.
You can use the kaminari gem.
Very ease to use and highly customization friendly.
Adding pagination to a Ruby on Rails app
To add pagination to your Ruby on Rails app, you have to modify the following files:
Gemfile:
gem 'will_paginate', '~> 3.1.0'
gem 'will_paginate-bootstrap'
Areas controller --> index
#areas = Area.pagination_request(params[:page])
index.html.erb
<%= will_paginate #areas, renderer: BootstrapPagination::Rails %>
Model file:
def self.pagination_request(page)
paginate :per_page => 10, :page => page
end
Or, small, swifd & actively maintained: Pagy

Resources