find_by_sql renders an array - sql

I got some problems here, I can't make my find_by_sql request to render an ActiveRecord relation. Indeed, I need an activerecord relation to make a new request:
#searches = #searches.find_by_sql('SELECT *, COUNT( follower_id ) FROM follows GROUP BY followable_id LIMIT 0 , 3') if params[:only_famous_projects]
#project_pages = #project_pages.where(:project_id => #searches.pluck(:'followable.id')) if params[:only_famous_projects]
I can't use "pluck" without an activerecord relation. Therefore, I think I have to convert my sql request to an Activerecord request. However, as soon as I use "count" on ActiveRecord, I have an huge problem: I don't have on the end an ActiveRecord relation, but a FixNum!
I don't know where to find the answer anymore, I would be really gratefull if you could help me.
Thanks

find_by_sql will return an ActiveRecord object only if you call it with YourModel.find_by_sql.
Why not use the ActiveRecord query interface. It does a good job with calculations.
UPDATED
#searches = #searches.group(:followable_id).limit(3).offset(0).count(:follower_id) if params[:only_famous_projects]
Notice that it will give you a Hash containing the count for each followable_id.
Isn't LIMIT 0, 3 equivalent to LIMIT 3 ?

COUNT will always return a FixNUM, because you asked the database to count the number of rows.
You should really try to use find_by_sql as a last resort as it is only meant to bypass ActiveRecord for things that it can not do. And even for things that ActiveRecord doesn't support, you can always see if you can use the Squeel or Valium gems to handle edge-cases.
Another reason not to use find_by_sql is that, for example, using MySQL specific terms will lock you out of using other databases in the future.

Related

how active record sample works?

I happened to see a line
Item.where(conditions).limit(10).order('created_at desc')
And I wonder it is similar to
Item.where(conditions).order('created_at desc').limit(10)
Seems ok as per new changes to rails 3, active record
But how about if we want, sample 10 items, *ordered by created_at*. This was my question #1
question 2 is 'limit' works within query, 'sample' does not..right?, it seems to be taken care of ruby array sample..right?
I recommend you check the railscast episodes 239 active relation walkthrough to understand how it works.
Basically, the internal work is done by Arel gem, activerecord object simply maintains the arel object when you do chaining. By the end of the day, it lets arel generate the sql statement, then it calls the find_by_sql with the generated sql statement.
The thing is that whether you called order before limit or the other way round, the generated sql statement is simply the same. Everything is executed by the database backend, activerecord simply parse the results and build up objects back.

Ruby followers query

We have an SQL query in our Rails 3 app.
#followers returns an array of IDs of users following the current_user.
#followers = current_user.following
#feed_items = Micropost.where("belongs_to_id IN (?)", #followers)
Is there a more efficient way to do this query?
The query you have can't really be optimized anymore than it is. It could be made faster by adding an index to belongs_to_id (which you should almost always do for foreign keys anyway), but that doesn't change the actual query.
There is a cleaner way to write IN queries though:
Micropost.where(:belongs_to_id => #followers)
where #followers is an array of values for belongs_to_id.
It looks good to me.
However if you're looking for real minimum numer of characters in the code, you could change:
Micropost.where("belongs_to_id IN (?)", #followers)
to
Micropost.where("belongs_to_id = ?", #followers)
which reads a little easier.
Rails will see the array and do the IN.
As always the main goal of the ruby language is readability so little improvements help.
As for query being inefficent, you shuld check into indexs on that field.
They tend to be a little more specific for each db - you have only specified generic sql. in your question.

how do I write SQL in a ruby method?

I would like to have a method called feed in my User model that returns all the entries from two tables (discussions, exchanges).
In User.rb
def feed
SELECT * FROM discussions, exchanges GROUP BY created_at
end
This doesn't work, i get a problem in rails console
syntax error, unexpected ';', expecting '='
Can anyone show me how to write SQL in here? Basically I want to return and sort entries from two different tables..
if you want actual ActiveRecord objects you can try the following
def feed
exchanges = Exchange.all
discussions = Discussion.all
(exchanges + discussions).sort! { |a, b| a.created_at <=> b.created_at }
end
this is quite ineffective, as the sorting could be done in sql, but ActiveRecord cannot instantiate records selected from different tables (you can somehow override this by using STI)
Firstly - you can't just write plain SQL in your ruby code and expect it to work.
It's ruby, not SQL. They are different languages.
If you can - use the ruby-way with associations instead (as per the other example).
However - if you desperately need to use raw SQL (eg you have legavy tables that don't match to models or have some complex combination-logic in teh SQL that doesn't easily map to assocations); then you need to pass SQL to the database... which means using a connection via Active Record.
Try:
def feed
ActiveRecord::Base.connection.execute("SELECT * FROM discussions, exchanges GROUP BY created_at")
end
It will not return ruby models for you - just a raw results-object.
I'd recommend trying this in script/console and then doing a "puts my_user.feed.inspect" to have a look at the kind of thing it returns so you know how to use it.
Note: the presence of this kind of thing is considered a strong code smell - only use it where you really need it

How to find the record with the maximum price?

This returns the maxium value, not the complete record:
self.prices.maximum(:price_field)
And currently, I find the record like this:
def maximum_price
self.prices.find(:first, :conditions => "price = #{self.prices.maximum(:price_field)}" )
end
Is this the correct way ? Because the above needs two SQL statements to make it work, and it somehow does not feel right.
Ps. Additionally, I want that if more than one record has the same "maximum" value, then it should get the one with the latest updated_at value. So that would mean another SQL statement??
Pps. Does anyone know of a good or detailed reference for AREL and non-AREL things in Rails? The Rails Guide for ActiveRecord query is just not enough!
(I'm using Rails 3)
===UPDATE===
Using AREL I do the following:
self.prices.order("updated_at DESC").maximum(:price_field)
But this only gives the maximum value, not the complete record :(
Also, is the use of maximum() really AREL?
How about something like this?
self.prices.order("price DESC").first

Changing Rails Query to Pure SQL

I'm looking to move the following code away from ActiveRecord to pure SQL for a performance increase. What would be the best way to write this query in pure SQL (MySQL DB)?
User.count(:conditions => ["email = ?",params[:email]]) > 0
Thanks
Analogously to find_by_sql you can use count_by_sql:
User.count_by_sql(["SELECT COUNT(*) FROM users u WHERE u.email = ?", params[:email]]) > 0
Remember also to use the syntax ["... ? ...", var] here to protect against SQL injection.
However, I doubt that you can achieve a significant performance improvement by that. Test it. If it's not faster, stay with the ActiveRecord version or try to find a more nifty solution to your problem.
Edit:
If you just want to test whether an given email is already contained in the table you could also test the performance of User.find_by_email(params[:email]).present?