Rails 4 how to calculate sum for each method - ruby-on-rails

I'm trying to find solution how to calculate sum for each method in rails.
I feel like tried hundreds different approaches, but couldn't quiet figure it out yet.
For example:
Helper return 2 ids for products: (using alert just to make it visible)
view_context.time_plus.each
returns 1,2
But when I combine it with call, and selecting multiple option it is only returns last one instead of sum for both value
view_context.time_plus.each do |i|
flash[:alert] = [Service.find_by_price_id(i).time].sum
end
I see in logs call made for both value:
Service Load (0.2ms) SELECT `services`.* FROM `services` WHERE `services`.`price_id` = 0 LIMIT 1
Service Load (0.2ms) SELECT `services`.* FROM `services` WHERE `services`.`price_id` = 1 LIMIT 1

find_by_column always returns only one record.
You can use where condition for multiple ids like this
Model.where(column_name: [array_of_ids])
if view_context.time_plus returns an array of ids
Service.where(price_id: view_context.time_plus]).sum(:time)

You can try this
flash[:alert] = view_context.time_plus.map{|i| Service.find_by_price_id(i).time}.sum
This should work

You can use inject method:
sum = view_context.time_plus.inject(0) { |sum,i| sum+=Service.find_by_price_id(i).time }
But if you are using ruby on rails, better way is use active_record and sql:
sum2 = Service.where(price_id: [1,2]).sum(:time)

Related

How to combine 3 SQL request into one and order it Rails

I'm creating filter for my Point model on Ruby on Rails app. App uses ActiveAdmin+Ransacker for filters. I wrote 3 methods to filter the Point:
def self.filter_by_customer_bonus(bonus_id)
Point.joins(:customer).where('customers.bonus_id = ?', bonus_id)
end
def self.filter_by_classificator_bonus(bonus_id)
Point.joins(:setting).where('settings.bonus_id = ?', bonus_id)
end
def self.filter_by_bonus(bonus_id)
Point.where(bonus_id: bonus_id)
end
Everything works fine, but I need to merge the result of 3 methods to one array. When The Points.count (on production server for example) > 1000000 it works too slow, and I need to merge all of them to one method. The problem is that I need to order the final merged array this way:
Result array should start with result of first method here, the next adding the second method result, and then third the same way.
Is it possible to move this 3 sqls into 1 to make it work faster and order it as I write before?
For example my Points are [1,2,3,4,5,6,7,8,9,10]
Result of first = [1,2,3]
Result of second = [2,3,4]
Result of third = [5,6,7]
After merge I should get [1,2,3,4,5,6,7] but it should be with the result of 1 method, not 3+merge. Hope you understand me :)
UPDATE:
The result of the first answer:
Point Load (8.0ms) SELECT "points".* FROM "points" INNER JOIN "customers" ON "customers"."number" = "points"."customer_number" INNER JOIN "managers" ON "managers"."code" = "points"."tp" INNER JOIN "settings" ON "settings"."classificator_id" = "managers"."classificator_id" WHERE "points"."bonus_id" = $1 AND "customers"."bonus_id" = $2 AND "settings"."bonus_id" = $3 [["bonus_id", 2], ["bonus_id", 2], ["bonus_id", 2]]
It return an empty array.
You can union these using or (documentation):
def self.filter_trifecta(bonus_id)
(
filter_by_customer_bonus(bonus_id)
).or(
filter_by_classificator_bonus(bonus_id)
).or(
filter_by_bonus(bonus_id)
)
end
Note: you might have to hoist those joins up to the first condition — I'm not sure of or will handle those forks well as-is.
Below gives you all the results in a single query. if you have indexes on the foreign keys used here it should be able to handle million records:
The one provided earlier does an AND on all 3 queries, thats why you had zero results, you need union, below should work. (Note: If you are using rails 5, there is active record syntax for union, which the first commenter provided.)
Updated:
Point.from(
"(#{Point.joins(:customer).where(customers: {bonus_id: bonus_id).to_sql}
UNION
#{Point.joins(:setting).where(settings: {bonus_id: bonus_id}).to_sql}
UNION
#{Point.where(bonus_id: bonus_id).to_sql})
AS points")
Instead you can also use your 3 methods like below:
Point.from("(#{Point.filter_by_customer_bonus(bonus_id).to_sql}
UNION
#{Point.filter_by_classificator_bonus(bonus_id).to_sql}
UNION
#{Point.filter_by_bonus(bonus_id).to_sql}
) as points")

Populate an active record collection with different SQLs on the same model in rails

I'm trying to populate an active record collection from several SQLs on the same model. The only thing that differs between the SQLs is the where clause. My models have a type_id. As an example I have
models = Model.where("type_id = ?", 1)
logger.debug 'models.count ' + models.count.to_s
m = Model.where("type_id = ?", 2)
models << m
logger.debug 'models.count ' + models.count.to_s
From that, my logfile shows me
SELECT COUNT(*) FROM "models" WHERE (type_id = 1)
models.count 1
SELECT COUNT(*) FROM "models" WHERE (type_id = 1)
models.count 1
The second SQL is not correct for my situation, I wanted
SELECT COUNT(*) FROM "models" WHERE (type_id = 2)
The only way I've found to get around this is to do Model.all, iterate over each and add the ones I want. This would be very time consuming for a large model. Is there a better way?
From the sounds of it, you're looking for any Model with a type_id of either 1 or 2. In SQL, you would express this as an IN subclause:
SELECT * FROM models WHERE type_id IN (1, 2);
In Rails, you can pass an array of acceptable values to a where call to generate the SQL IN statement:
Model.where(:type_id => [1, 2])
As stated by #ArtOfCode what you want is to do the query on one pass. That being said, what you are trying to do there won't work because when you are adding with << the object of your second query to the first one you are just appending the instance to the first collection. The object type of the resulting query is an ActiveRecord_Relation which happens to hold two instances of your custom models (in this case Model) but when you send / call count thats actually executing an ActiveRecord query.
How can you tell the difference? Well, if you do run that code you used and do:
models.count
You'll see that there's SQL executed for whatever the conditions of the query on models you did, however, if you do this:
models.length
You'll notice the result is 2, and the reason is because the length of the collection of your own objects which happens to be inside the ActiveRecord_Relation is indeed two, and that is what happens if you use <<; it'll add object instances to the relation but that does not mean that they are part of the query.
You could even do this:
models << Model.new
And calling models.length would effectively return 3 because that is the amount of instances of your model that are contained within the relation, again, not a part of the query. So as you can see you can even add new object instances which have not even been saved to the database.
TL;DR if you want to query objects that are stored in the database do it on the query itself, or chain conditions at once, but don't try to mix activerecord relation collections.

Retrieve database records in Rails

My model named Person contains 3 columns namely name,age,gender.
Now how to get all the rows if the gender = "male". I try to
fetch the data as shown below.
p = Person.find_by_gender("male")
The above statement properly worked. But it returns only 1 record. Because, the statement is converted to like following query.
SELECT "persons".* FROM "persons" WHERE "persons"."gender" = $1 LIMIT 1 [["gender", "male"]]
Due to limit is set to 1 it returns only 1 record. So, how to unset the limit? My requirement to get all the records in table if gender
matches "male".
use where
Person.where(gender: "male")
find method always returns only one record
In rails find methods always return single record that's why it returning single record.
Person.find_by_gender("male")
Use Where which give you array of matching records(which is ActiveRecord::Relation)
Person.where(:gender => "male")

Rails + Postgres: How to select count of how many records are updated or inserted?

So I have an update statement:
UPDATE billing_infos set card_number = ''
FROM orders
WHERE billing_infos.order_id = orders.id ...);`
How would I find the count of how many records are updated by this statement?
I'm doing this in my console through ActiveRecord::Base.connection.execute() so it's just returning a <PG::Result:0x007f9c99ef0370> object.
Anyone know how I could do this using SQL or a Rails method?
p = ActiveRecord::Base.connection.execute(<query>)
p.cmd_status
This gives the command status. Something like
UPDATE 16
For more methods on PG::Result, refer here
While solution showed by Vimsha will definitely work, there is also another solution (assuming you use recent enough pg), which could be a bit nicer:
with u as (
update ... returning 1
)
select count(*) from u;
That's one query, and it's technically a select, so you run it as any other select.
As mentioned in a comment of another answer, the easiest way is to use the cmd_tuples attribute of the result
result = ActiveRecord::Base.connection.execute("insert into tbl select 'test' col")
puts result.cmd_tuples
result
1

Can Rails cache in this situation?

> player.records
Record Load (0.5ms) SELECT * FROM `records` WHERE (`records`.player_id = 1)
> player.records.first(:conditions => {:metric_id => "IS NOT NULL"})
Record Load (0.5ms) SELECT * FROM `records` WHERE (`records`.player_id = 1 AND (`records`.`metric_id` = 'IS NOT NULL')) LIMIT 1
Is there a way to make the second query not hit the database, but use the cache instead? It seems a bit excessive for it to be hitting the database again when they data is already in memory.
I need both results. I'm aware that Ruby can iterate through the values, but I'd prefer to do this through ActiveRecord if possible. I'm coming from a Django background where filter() did this just fine.
I'm using Rails 2.3.
No, simply because the condition is different.
But try to explain the context. Why do you need to use both queries? Can't you use only the second one?
If you need both, why can't you filter the Array with Ruby code instead of making another query?

Resources