How can I get FOUND_ROW()s from an active record object in rails? - mysql

When querying the database with:
#robots = Robot.all(:condition => [:a => 'b'], :limit => 50, :offset => 0)
What is the best way to get the total number of rows without the :limit?
In raw MySQL you could do something like this:
SELECT SQL_CALC_FOUND_ROWS * FROM robots WHERE a=b LIMIT 0, 50
SELECT FOUND_ROWS();
Is there an active record way of doing this?

This works for me:
ps = Post.all(:limit => 10, :select => "SQL_CALC_FOUND_ROWS *")
Post.connection.execute("select found_rows()").fetch_hash
=> {"found_rows()"=>"2447"}
This will probably not work for joins or anything complex, but it works for the simple case.

Robot.count actually is the solution you want.
Reading one of the comments above, it looks like you may have a misunderstanding of how .count works. It returns a count of all the rows in the table only if there's no parameters.
but if you pass in the same conditions that you pass to all/find eg:
Robot.count(:conditions => {:a => 'b'})
.count() will return the number of rows that match the given conditions.
Just to be obvious - you can even save the condition-hash as a variable to pass into both - to reduce duplication, so:
conds = {:a => 'b'}
#robots = Robot.all(:conditions => conds, :limit => 50)
#num_robots = Robot.count(:conditions => conds)
That being said - you can't do an after-the-fact count on the result-set (like in your example). ie you can't just run your query then ask it how many rows would have been found. You do actually have to call .count on purpose.

search = Robot.all(:condition => ["a=b"], :offset => 0)
#robots = search[0..49]
#count = search.count
That should get what you want, gets all the Robots for counting and then sets #robots to the first 50. Might be a bit expensive on the resource front if the Robots table is huge.
You can of course do:
#count=Robot.all(:condition => ["a=b"], :offset => 0).count
#robots=Robot.all(:condition => ["a=b"], :limit => 50, :offset => 0)
but that will hit the database twice on each request (although rails does have query caching).
Both solutions only use active record so are database independent.
What do you need the total returned by the query for? if its pagination look into Will_paginate (Railscast) which can be extended with AJAX etc...

Try find_by_sql may that help.

Is #robots.size what you're looking for? Or Robot.count?
Otherwise, please clarify.

I think hakunin is right.
You can get no of row return by query by just chekcing the size of resulting array of query.
#robots = Robot.find_by_sql("Your sql")
or
#robots = Robot.find(:all , :conditions=>["your condiitons"] )
#robots.size or #robots.count

Related

ThinkingSphinx::OutOfBoundsError configuration confusion

Hitting an OutOfBoundsError as a consequence of misunderstanding the proper configuration syntax (which may also be a by-product of legacy syntax).
The manual suggests a Class search taking on WillPaginate styled parameters. Having many fields to draw from, the model is defined as
class AziendaSearch < BaseSearch
set_per_page 10000
accept :terms
end
the set_per_page was put at a high level because if I set it at the target of 100, the will_paginate links do not show up.
the controller may be excessively convoluted to include the ordering parameter, and thus result in a two-step process:
#azienda_search = AziendaSearch.new params
#results = #azienda_search.search
#aziendas = Azienda.order('province_id ASC').where('id IN (?)', #results).paginate :page => params[:page], :per_page => 100
the view paginates on the basis of #aziendas:
<%= will_paginate #aziendas, :previous_label => "precedente ", :next_label => " successiva" %>
My suspicion is that the search model is not properly set, but the syntax is not obvious to me given the manual's indications. page params[:page] certainly does not work...
Update
BaseSearch is a Sphinx method and was in fact inherited from an older version of this applications (rails2.x...). So this may be hanging around creating all sort of syntaxic confusion.
In fact, following the manual, I am now fully uncertain as to how to best makes these statements. Should a seperate class be defined for AziendaSearch ? If not, where should the Azienda.search bloc be invoked... in the controller as such?
#azienda_search = Azienda.search(
:max_matches => 100_000,
:page => params[:page],
:per_page => 100,
:order => "province_id ASC"
)
#results = #azienda_search.search
I'm not sure what BaseSearch is doing with set_per_page (that's certainly not a Thinking Sphinx method), but it's worth noting that Sphinx defaults to a maximum of 1000 records. It is possible to configure Sphinx to return more, though - you need to set max_matches in your config/thinking_sphinx.yml to your preferred limit (per environment):
production:
max_matches: 100000
And also set the limit on the relevant search requests:
Azienda.search(
:max_matches => 100_000,
:page => params[:page],
:per_page => 100
)
As for the doubled queries… if you add province_id as an attribute in your index definition, you'll be able to order search queries by it.
# in your Azienda index definition:
has province_id
# And then when searching:
Azienda.search(
params[:azienda_search][:terms],
:max_matches => 100_000,
:page => params[:page],
:per_page => 100,
:order => "province_id ASC"
)

How to lazy-load or eager-load the sum of associated table?

We build some object in our controller:
#sites = Site.find(:all, :conditions => conditions, :order => 'group_id ASC')
And then in the view (currently), we are doing something like:
#sites.each do |site|
%p Site Name
= site.name
- actual = site.estimates.where('date<?', Date.today).sum(:actual_cost)
%p
= actual
end
Basically like that.
And of course this fires off a query for the Sites and then a query for N sites returned. I know about eager-loading associations with #sites = Site.find(:all, :include => :estimates) but in this case it didn't matter because we're doing the SUM on the query which is special it seems.
How would I eager load the SUMs in such that I don't get all the crazy queries? Currently it's over 600...
provide your conditions & ordering in this query only, which will push the result into a Hash.
sites = Site.includes(:estimates).where('estimates.date < ? ', Date.today)
.order('sites.id ASC')
actual = 0
sites.map { |site|
site.estimates.map { |estimate| actual = actual + estimate.actual_cost }
}
From your explanation, I am assuming actual_cost is a column in estimate table.

problem with ActiveRecord/Rails ordering of query data from MySQL -- no problem in SQLite

I have the following two ActiveRecord queries in an application_helper.rb file:
#left_menu = Page.select('id, menu_name').where(:published => true, :left_menu => true).order("sort")
Also can be written as:
#left_menu = Page.select('id, menu_name').where(:published => true, :left_menu => true).order("'sort' ASC")
and:
#left_menu = Page.find(:all, :conditions => {:published => true, :left_menu => true}, :order => :sort)
Why does the first one fail to sort on the 'sort' column, while the second one does not? Both work in SQLite, but only the second one works in MySQL.
Any ideas?
it's the quote in ther order params .
the query generated will be (similar to)
"SELECT id, title FROM `pages` WHERE (`pages`.`pub` = 1) ORDER BY 'sort' ASC"
its the char ' quote . It's wrong sql syntax , it's going to order by costant value not column value . sqlite allow it , mysql not.
try to simple use
Page.select('id, menu_name').where(:published => true, :left_menu => true).order("sort ASC")
without single quote in the order chain method parameters.
sorry for my english.
have a nice day

Make a Mysql query that finds data just before another (in RoR)

I'm making this query to MySql
Image.find( :all,
:conditions => ["created_at > ? && approved = 1", #image.created_at],
:order => "created_at DESC", :limit => 5)
However, I want the images create just before the given image was created at. Right now, it's returning a list of images from the top of the list, that were create much, much before that image. How can I do this?
Your current query will find any images newer than #image because your using >. You'll need to decide what kind of range you're wanting to look in.. What time frame do you consider "just before"? Minutes? Seconds?
To find all images created within 5 mins of #image, try:
Image.find(:all, :conditions => ["(created_at > ? and created_at < ?) and approved = 1", #image.created_at.advance(:minutes => -5), #image.created_at])
My solution feels more like a hack, but I'm not sure of a better one. I set :order => "created_at ASC"... and then reversed the resulting array, and got the response I wanted. Hrm.

ActiveRecord select attributes without AR objects

I have a table with MANY rows, I need just the IDs of certain rows.
The slow way is to call
SomeARClass.find(:all, :conditions => {:foo => true}, :select => :id)
This returns AR Objects...
Is there a way to call a select on a class and have it return a plain old ruby data structure. Something like this:
SomeARClass.select(:id, :conditions => {:foo => true})
-> [1,2,3]
ActiveRecord::Base.connection.select_all(sql)
or
SomeARClass.connection.select_all(sql)
This is what you want to use. It returns an array of hashes. It should be used sparingly though. Hand coded sql is what ActiveRecord was built to replace. I only use in in really performance critical areas where constructing and returning AR objects is too slow.
The pluck method is exactly what you want, without compromising the SQL bad practice:
SomeARClass.where(:foo => true).pluck(:id)
I believe this should be the selected answer!
I don't think there is anything like SomeARClass.select(:id, :conditions => {:foo => true})
but you have two options
SomeARClass.find(:all, :conditions => {:foo => true}, :select => :id).map(&:id)
#=> [1,2,3,4]
id_hash = ActiveRecord::Base.connection.select_all('select id from tablename')
#=> [{"id"=>"1"}, {"id"=>"2"}, {"id"=>"3"}, {"id"=>"4"}]
id_hash.map(&:values).flatten
#=> ["1", "2", "3", "4"]
The second option returns only a hash and not Active record objects but it does looks a bit hackish.
Short answer:
Use .ids to fetch only ids
Use pluck to fetch ONLY some columns, but values will be PARSED by Active Record
Use ActiveRecord::Base.connection.select_all to fetch UNPARSED values.
Notes:
There is small difference between pluck and select_all:
If you pluck data - ActiveRecord map data to Ruby objects, like Dates, Enums,
Models, etc, and it could be different than you expect.
You can notice that the result below is a string:
2.5.1 :001 > ActiveRecord::Base.connection.select_all("select created_at from some_ar_class where id = 1 limit 1").rows.first
(0.6ms) select created_at from some_ar_classes where id = 1 limit 1
=> ["2018-10-01 01:12:31.758161"]
While pluck return Dates
2.5.1 :002 > SomeARClass.where(id: 1).pluck(:created_at).first.class
(0.4ms) SELECT "some_ar_classes"."created_at" FROM "some_ar_classes" WHERE "some_ar_classes"."id" = $1 [["id", 1]]
=> ActiveSupport::TimeWithZone
The same happens with Enums(int in database, but :symbols in Ruby)
And all this parsing/mapping operations also takes time. It's not a lot, but still. So if you asking for the most fast way to fetch data - its definitely raw sql query with connection.select_all, but there are very small situations when you can get some significant performance increase on that.
So my recommendation is using pluck.