Rails3/ActiveRecord: select sum with parameters? - sql

Give the following (already simplified) query in SQLite:
def self.calculate(year, month, user_id, partner_id)
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
group("strftime('%Y-%m', date)")
end
The full query has more sums with different case when statements and some of them depend on whether it is user_id oder partner_id. Unfortunately, Rails complains as select does not take the second parameter with the substitutions like where does. Is there any way to achieve what I want without running two queries, one for user_id and one for partner_id?

One can be so blind....instead of:
select('sum(case when joint = "f" and user_id = :user_id then amount_calc else 0 end) as sum_single' , {
:user_id => user_id
}).
just build the string:
select('sum(case when joint = "f" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_single').
As nobody answered, this is for the archives :)
Edit: Sorry, beware of that: as noted below, this is vulnerable.

Related

Rails Arel equivalent of this complex sql query

Here is the original logic
(scrape_datas = ScrapeData.find(
:all, :conditions =>
"artist_status = 'NOT_FOUND'
AND blacklisted = 1
AND extracted = 0
and not EXISTS(
SELECT * FROM artist_name_suggestions where original = artist_name
)
I've been able to split up the first part better
scrape_datas = ScrapeData.where(
:artist_status => 'NOT_FOUND',
:blacklisted => 1,
:extracted => 0
)
Although having issues getting the "and not EXISTS" query into the mix
and not EXISTS(
SELECT * FROM artist_name_suggestions where original = artist_name
)
Thanks!
Firstly you can extract simple scopes:
scope :not_found, where(:artist_status => 'NOT_FOUND')
scope :blacklisted, where(:blacklisted => 1)
scope :extracted, where(:extracted => 0)
Then add a query method (assume artist_name is a column of scrape_datas):
def self.no_suggestions
scrape_datas = ScrapeData.arel_table
suggestions = ArtistNameSuggestion.arel_table
where(ArtistNameSuggestion.where(
suggestions[:original].eq(scrape_datas[:artist_name])
).exists.not)
end
Now you can do something like this:
ScrapeData.not_found.blacklisted.extracted.no_suggestions

Nhibernate QueryOver - .SelectCount() with predicate

I need to select count of row with a condition:
Query to collect the full count:
var searchs = searchQuery.SelectList
(list => list
.SelectGroup(order => order.Id).WithAlias(() => groupResult.GlobalId)
.SelectCount(() => _transaction.ReturnStatus).WithAlias(() => groupResult.DeclineCount)
)
I need count of transactions that equals 201. Something like this:
.SelectCount(() => _transaction.ReturnStatus == 201).WithAlias(() => groupResult.DeclineCount) //runtime error
Thanks in advance!
PS:
Original SQL Query:
SELECT TOP 100
globalOrd.ID AS GlobalId ,
SUM(CASE WHEN transact.returnStatus = 201 THEN 1
ELSE 0
END) AS DeclineCount
FROM Orders.Global globalOrd
INNER JOIN Orders.TransactionDetail transactDet ON globalOrd.ID = transactDet.DetailID
INNER JOIN Orders.[Transaction] transact ON transactDet.TransactionID = transact.ID
GROUP BY globalOrd.ID
If you don't need the total count in the same query you can simply add in the restriction before the SelectList:
var searchs = searchQuery.SelectList
(list => list
.Where(() => _transaction.ReturnStatus == 201)
.SelectGroup(order => order.Id).WithAlias(() => groupResult.GlobalId)
.SelectCount(() => _transaction.ReturnStatus).WithAlias(() => groupResult.DeclineCount)
)
If however, you want both the total and the restricted count, you would have to use a SqlProjection for the latter doing something like:
SUM(CASE {alias}.ReturnStatus WHEN 201 THEN 1 ELSE 0 END)

Adding more than one string to a controller find condition?

I have the following code in one of my Rails (3.1) controllers:
#count = Call.where(:destination => current_user.destination_id).count
#count_day = Call.where('created_at > ?', 1.days.ago).count
#count_month = Call.where('created_at > ?', 30.days.ago).count
They are all working as expected, but I am trying to somehow merge two of them together so it shows the count of calls created in the last 1 day, but only for calls with a destination that matches the current users destination_id value.
I have tried to add :condition but with no joy:
#count_day = Call.where(:destination => current_user.destination_id, :condition ['created_at > ?', 1.days.ago]).count
Is it possible to add more than one condition in this way? If so, how?
Try this:
#count_day = Call.where("destination = :destination AND created_at > :date", { :destination => current_user.destination_id, :date => 1.days.ago}).count
Where creates a scope and scopes can be chained, so you can do
Call.where(:destination =>current_user.id).where("created_at > ?", 1.day.ago).count

no id for model?

I get the error
undefined local variable or method `id' for #<Class:0x007fe1dc4e3bb0>
when defining a sql call in my model user:
RECENT_EVENTS_CONDITION = "(user_id = #{id}) OR user_id IN (SELECT user_b_id AS user_id FROM user_follows WHERE user_follows.user_a_id = #{id} )"
Model code
has_many :recent_events,
:class_name => "Activity",
:finder_sql => 'SELECT activities.* FROM activities
WHERE ' + RECENT_EVENTS_CONDITION + '
ORDER BY activities.created_at DESC',
:counter_sql => 'SELECT COUNT(*) FROM activities
WHERE ' + RECENT_EVENTS_CONDITION
Everything works fine until i introduce this recent_events ...
I'm assuming this is a User class or something similar, and I'm assuming when you use #{id}, you're really just trying to use the user's id. So I would do this instead:
RECENT_EVENTS_CONDITION = "(user_id = users.id) OR user_id IN (SELECT user_b_id AS user_id FROM user_follows WHERE user_follows.user_a_id = users.id )"

pulling one field of a record into an array in a Rails 3 app

Rails 3 noob here. Currently the code in my controller below is getting the whole record in my database. I am trying to populate the array with one integer, not the whole record. The integer is contained in a table "answers" and the field name is "score". How can i modify this line in my controller to get just the one field?
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id )
end
UPDATE FOR CLARIFICATION: The :score can be any integer from 0 to 5. I would like to populate #ans[s.position] with the integer.
Thanks.
You're very close
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? and user_id = ?",s.q_id,current_user.id).select(:score).first.try(:score)
end
You need to select "score" from Answer, then you need to retrieve it from the object.
Since where could potentially return many Answers, use first to pull off the first Answer, and then score to project out the score field.
answers = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id )
score = answers.first.score
All together it would be:
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id ).first.score
end
As an optimization to only retrieve score from the database, instead of answers.*, you could use select(:score).
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id ).select(:score).first.score
end
See Active Record Query Interface for more about using select.
Just include them.
#questions = Question.includes(:answers).select(:position)
Or use this
#questions = Question.select(:position)
#questions.each{ |s| #ans[s.position] = s.answers.where(:user_id => current_user.id) }