Reputation: 2448
Basically I have a Driver
model that has many rides
. Those rides
has price
field and I want to calculate driver's total_paid
(the payment they have earned for all the time) and this_week_paid
(the payment has been done only from the beginning of this week to the end of it) in one active record query.
I have achieved the correct number for total_paid
part easily with one join like this:
Driver.joins(:rides).
select("#{Driver.table_name}.*, sum(substring(rides.price from '[0-9]+.[0-9]*')::numeric) as total_paid").
group("#{Driver.table_name}.id").
order("total_paid DESC, id")
Now when I try to add this_week_paid
to that query:
Driver.joins("INNER JOIN rides this_week_rides ON #{Driver.table_name}.id = this_week_rides.driver_id").
joins("INNER JOIN rides all_rides ON #{Driver.table_name}.id = all_rides.driver_id").
select("#{Driver.table_name}.*, " +
"sum(substring(this_week_rides.price from '[0-9]+.[0-9]*')::numeric) as this_week_paid, " +
"sum(substring(all_rides.price from '[0-9]+.[0-9]*')::numeric) as total_paid").
where(this_week_rides: { created_at: Time.current.beginning_of_week..Time.current.end_of_week }).
group("#{Driver.table_name}.id").
order("this_week_paid DESC, id")
It runs without throwing any exceptions however, interestingly the total_paid
field is two times of correct number and this_week_paid
field is three times of the correct one ( Query answer: { this_week_paid: 188.46, total_paid: 159.9 }
, the correct answer: { this_week_paid: 62.82, total_paid: 79.95 }
).
I did try to add where("this_week_rides.id != all_rides.id")
and it gives me another wrong result ("this_week_paid" => 125.64,"total_paid" => 97.08
)
What am I missing?
Upvotes: 2
Views: 65
Reputation: 3773
You join the same table twice and that will multiply the number of rows you get so that is why you get multiples of the expected result. Just join it once and filter in the select like this:
sum(substring(rides.price from '[0-9]+.[0-9]*')::numeric) filter (
where rides.created_at between time1 and time2
) as this_week_paid,
sum(substring(rides.price from '[0-9]+.[0-9]*')::numeric) as total_paid
Upvotes: 2