I have included client_person and case_histories but when querying case_histories it still generates n+1 query.
I have tried for a long while but nothing seems to work.
#client_records = #records.includes({person: [:client_person, :case_histories]})
In person model:
belongs_to :client_person, class_name: 'Client::Person'
has_many :case_histories, through: :region_profiles, class_name: 'Admin::PeopleCaseHistory'
the query that generates n+1:
#client_records.each do |record|
record.person.case_histories.select{ |hist| hist.case_type == 2}.first
end
Specifically, after correctly loading the case_histories for all clients in the list(resulting from the includes clause), it still one by one queries for the case_histories of each client.
The first query generated by includes:
Admin::PeopleCaseHistory Load (46.9ms) SELECT "case_histories".* FROM "case_histories" WHERE "case_histories"."region_profile_id" IN (411, 16804, 572, 19506, 16539, 692, 4828)
The subsequent N+1 queries:
Admin::PeopleCaseHistory Load (29.5ms) SELECT "case_histories".* FROM "case_histories" INNER JOIN "region_profiles" ON "case_histories"."region_profile_id" = "region_profiles"."id" WHERE "region_profiles"."person_id" = $1 [["person_id", 9867]]
Admin::PeopleCaseHistory Load (34.3ms) SELECT "case_histories".* FROM "case_histories" INNER JOIN "region_profiles" ON "case_histories"."region_profile_id" = "region_profiles"."id" WHERE "region_profiles"."person_id" = $1 [["person_id", 430]]
Please advise!
Thanks
Looks like you have both people_region_profile_id and region_profile_id on your case_histories table. One of those is getting used during eager-loading, but the other is not.
It would help for you to list out your entire model/relation graph because I'm unable to construct what exactly is going on. It seems to me that your graph looks something like this:
class Person < ActiveRecord::Base
belongs_to :client_person
has_many :region_profiles
has_many :case_histories, through: :region_profiles
end
class RegionProfile < ActiveRecord::Base
belongs_to :person
has_many :case_histories
end
class CaseHistory < ActiveRecord::Base
belongs_to :region_profile
end
But clearly I'm missing something if case_histories have two different foreign keys.
Related
Here what I have now :
class Pokemon < ActiveRecord::Base
has_many :pokemon_moves, dependent: :destroy
has_many :moves, through: :pokemon_moves
end
class PokemonMove < ActiveRecord::Base
belongs_to :pokemon
belongs_to :move
end
class Move < ActiveRecord::Base
belongs_to :type
end
And many others, no important.
I just would like to search for a Pokémon which could have Move named "pound" and another Move "mega-punch"
I tried :
Pokemon.joins(:moves).where(moves: {name: 'pound'}).where(moves: {name: 'mega-punch'})
But no result. The translated SQL is :
SELECT "pokemons".* FROM "pokemons" INNER JOIN "pokemon_moves" ON "pokemon_moves"."pokemon_id" = "pokemons"."id" INNER JOIN "moves" ON "moves"."id" = "pokemon_moves"."move_id" WHERE "moves"."name" = $1 AND "moves"."name" = $2 [["name", "pound"], ["name", "mega-punch"]]
If I only search for one move, it works just fine, but I can't get it with two moves.
I have tried many things, but all concludes to bad results.
Of course, I have a Pokémon that have those moves, if I do Pokemon.find_by_name('golurk').moves I can retrieve those two moves.
Thanks !
UPDATE 1 :
I made it work by simply using & operator :
Pokemon.joins(:moves).where(moves: {name: 'pound'}) & Pokemon.joins(:moves).where(moves: {name: 'mega-punch'})
But it's really not efficient, and I'm pretty sure we can find a better way.
The reason you get no results is because you are searching for moves with the name pound AND the name mega punch.
Change your code to:
Pokemon.joins(:moves).where(moves: {name: ['pound', 'mega-punch']})
Chaining where results in AND in your sql. If you want OR, either use an array like in the example above, or write your own sql.
I have two models, ParentProfile and RoomParentAssignment:
class ParentProfile < ActiveRecord::Base
has_many :room_parent_assignments
and
class RoomParentAssignment < ActiveRecord::Base
belongs_to :room_parent_profile, class_name: 'ParentProfile', foreign_key: 'parent_profile_id'
I would like to retrieve all ParentProfile records where there are no room_parent_assignments. I'm looking for something like the following statement (which needless to say, is invalid):
#non_room_parents = ParentProfile.where(!room_parent_assignments.present?)
How would I do this?
The below query should do
ParentProfile.joins("left outer join room_parent_assignments on room_parent_assignments.parent_profile_id = parent_profiles.id").where(room_parent_assignments: {parent_profile_id: nil})
use the below code:
parent_ids = RoomParentAssignment.select(parent_profile_id)
#non_room_parents = ParentProfile.where.not(:id => parent_ids)
You have two options here:
OPTION 1
#non_room_parents = ParentProfile.where.not(id: RoomParentAssignment.all.map(&:parent_profile_id))
OPTION 2
#non_room_parents = ParentProfile.where("id NOT IN (?)", RoomParentAssignment.all.map(&:parent_profile_id))
Both of them are equal to get no parent rooms.
I have 2 models:
DeliverySlot has_many :orders
Order belongs_to :delivery_slot
Delivery Slots have a limit of how many orders they can hold. I want to create a scope to give all the available delivery slots. An available delivery slot is one that hasn't reached it's limit of associated orders.
My attempt looks like:
scope :available, where("limit > ?", order.count).joins(:orders)
order.count is pseudocode above.
To do this like you have setup you would need to use orders.count instead of order.count because you're referring to the association. This would prompt ActiveRecord to assemble a query that looks something like SELECT COUNT(*) FROM orders WHERE delivery_slot_id = 1.
Rails is actually smart enough to then use that as a subquery in your where condition when you pass it appropriately, a la where('limit > ', orders.count). But as you might see, this won't work if it's precompiled because the query uses an explicit ID in the condition.
What you need instead is to count orders with an ambiguous condition, then use it as a subquery: where('limit > ?', Order.where(delivery_slot_id: 'delivery_slots.id').count). If you tried to run the query for the order count on its own it would fail on delivery_slots, but because it's in the subquery here you should be smooth sailing.
I'd like to propose another way of doing this altogether though, using a counter cache:
class AddCounterCacheToDeliverySlots < ActiveRecord::Migration
class DeliverySlot < ActiveRecord::Base; end
def change
add_column :delivery_slots, :orders_count, :integer, default: 0
add_index :delivery_slots, [:orders_count, :limit]
DeliverySlot.reset_column_information
DeliverySlot.find_each do |delivery_slot|
DeliverySlot.update_counters delivery_slot.id, orders_count: delivery_slot.orders.count
end
end
end
class Order < ActiveRecord::Base
belongs_to :delivery_slot, counter_cache: true
end
class DeliverySlot < ActiveRecord::Base
has_many orders
scope :available, where('orders_count < limit')
end
Rails will automatically increment and decrement the orders_count column for each DeliverySlot, and because it's indexed, it's ridiculously fast to query.
scope :available, lambda {
|delivery_slot| joins(:orders).
where("limit > ?", order.count)
}
try this
So I found a way to do it in SQL. If anyone knows of a more ruby way without creating loads of database queries please jump in.
scope :available, joins('LEFT JOIN orders
ON orders.delivery_slot_id = delivery_slots.id')
.where("delivery_slots.limit > (
SELECT COUNT(*) FROM orders
WHERE orders.delivery_slot_id = delivery_slots.id )
")
I'm trying to use active-record query possible connections between airports.
I described the models I created already in another question here:
n:m self-join with ruby on rails active record
Basically, what I can do now is that:
ny = Airport.create({"city" => "New York"})
la = Airport.create({"city" => "Los Angeles"})
ny.destinations << la
la.destinations << ny
I ran into an issue querying the data I'm looking for, which is quite simple in SQL but I had no luck with active record yet.
ny = Airport.where('city = ?', 'New York')
ny.destinations
returns the correct objects, but all of them.
The SQL query looks like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3
I'd like to filter those results by cities starting with "s" for example, so an SQL query could look like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3 AND airports"."city" LIKE "s%"
I tried it this way:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
But I always get an empty result.
How could I use active record to filter my resulting objetcs?
edit: Thats the best solution I found so far:
I added the cityLike() method to the Airport model:
app/models/airport.rb:
class Airport < ActiveRecord::Base
attr_accessible :city, :name
has_many :connections
has_many :destinations, :through => :connections
has_many :inverse_connections, :class_name => "Connection", :foreign_key => "destination_id"
has_many :inverse_destinations, :through => :inverse_connections, :source => :airport
def self.cityLike(query)
where("city LIKE ?", "%#{query}%")
end
end
app/model/connection.rb:
class Connection < ActiveRecord::Base
attr_accessible :destination_id, :airport_id
belongs_to :airport
belongs_to :destination, :class_name => "Airport"
end
Now I can query the objects with the following statement:
Airport.find(1).destinations.cityLike("a")
Not sure if it's the best solution, but it produces the query I was looking for.
Thanks a lot to all af you!
ActiveRecord::Base.execute(sql) lets you use pure SQL to do your query and returns the relevant model.
What about this?
Airport.find(:all, joins: "INNER JOIN `connections` ON airports.id = connections.destination_id").where("connections.airport_id = ? AND airports.city LIKE ?", ny_id, "s%")
This code:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
works like this--first you have an object ny repesenting the city of New York. When you say ".destinations" you have now followed a relation you defined in your model to retrieve all the destinations that you can get to from New York. However, if I'm imagining your database schema correctly, these destinations don't actually have a field called "city"; instead, they have a destination_id, which ties the destination to a particular airport, and it's the airport that has a city associated with it.
So when you query the destination table for 'city LIKE ?', it doesn't find any matching records.
Instead, try
ny.destinations.joins(:airports).where('city LIKE ?', '#{params[:query]}%')
Given the following models:
class PeriodBilling < ActiveRecord::Base
belongs_to :appcode
belongs_to :period
belongs_to :sla
belongs_to :unit_type
belongs_to :dpc_employee
belongs_to :general_ledger
end
class GeneralLedger < ActiveRecord::Base
has_many :appcodes
has_many :sla_task_details
belongs_to :expense_category
has_many :period_billings
has_many :expected_billings
end
How would I find the following SQL equivalent in Rails, to display as a matrix with periods down the left side and general_ledgers across the top, with the sum(pb.current_amt) in the appropriate fields?
select pb.pe_number, gl.general_ledger_number, sum(pb.current_amt)
from period_billings pb, general_ledgers gl
where pb.sla_id = 21
and pb.general_ledger_id = gl.id
group by pb.pe_number, gl.general_ledger_number
In Active Record terms, this is kinda what I'm trying to find:
#sla = Sla.find(params[:id])
#period_billings = PeriodBilling.where("sla_id = ?", #sla.id).group_by(&:general_ledger_id)
#billing_sum = #period_billings.inject(0){|sum,billing| sum+billing.current_amt}
So, I want to find all period_billings for the selected sla, grouped by general_ledger_id to get the sum of the period_billing.current_amt for those records. Then, I want to put the 12 periods down the side to show the summed amount in its appropriate general_ledger column across the top.
Thanks in advance!!! :)
Just a wild little stab at what I suggest you do. Some things are better meant for direct sql, and when that arises, like you're requesting, it's best to just use sql.
ActiveRecord::Base.connection.execute <<-EOF
select pb.pe_number, gl.general_ledger_number, sum(pb.current_amt)
from period_billings pb, general_ledgers gl
where pb.sla_id = 21
and pb.general_ledger_id = gl.id
group by pb.pe_number, gl.general_ledger_number
EOF