I have these models:
car:
class Car < ActiveRecord::Base
has_many :car_services, dependent: :destroy
has_many :services, through: :car_services
end
car_service
class CarService < ActiveRecord::Base
belongs_to :car
belongs_to :service
validates_uniqueness_of :service_id, scope: :car_id
end
truck
class Truck < ActiveRecord::Base
has_many :truck_services, dependent: :destroy
has_many :services, through: :truck_services
end
truck_service
class TruckService < ActiveRecord::Base
belongs_to :truck
belongs_to :service
end
service
class Service < ActiveRecord::Base
has_many :car_services
has_many :cars, through: :car_services
has_many :truck_services
has_many :trucks, through: :truck_services
end
In controllers' actions (there are separated actions for cars and for trucks) I query the models like this:
@cars = Car.find_by_sql("SELECT ...")
...
@trucks = Truck.find_by_sql("SELECT ...")
and then in the respective views:
<% @cars.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
...
<% end %>
This procedure works very well for cars
, it's very fast.
Different action for trucks
, but the same procedure:
<% @trucks.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
...
<% end %>
Now, this procedure is very slow. When I tried to debug why (because as I mentioned, the procedure is same for both models, but for cars it's working very fast even though that in the car
model is 600k records and in the truck
"only" 300k), I spotted in the console log following information:
...
Service Load (159.1ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 35769
Service Load (166.8ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 45681
Service Load (151.4ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 50974
...
How is this possible? Why I don't see these lines also for cars
and I see them for truck
? Do I miss something? I was thinking about missing indexes in the MySQL tables, but there aren't used any.
I am fighting with this issue the whole Saturday, but I have no solution for this...
I would be very grateful for an advice how to fix this issue.
Thank you very much guys.
I will start with an advice : post your sql queries for others to understand exactly what you are doing and to be able to help you
To eliminate N+1 you should use includes or preload
In your controllers do this:
@cars = Car.includes(:services).all
respectively,
@trucks = Truck.includes(:services).all
You can see that I preferred a much cleaner syntax to find_by_sql.
When you run:
@cars = Car.includes(:services).all
this will trigger 3 queries:
SELECT "cars".* FROM "cars";
SELECT "car_services".* FROM "car_services" WHERE "car_services".car_id IN (?, ?, ? ...);
SELECT "services".* FROM "services" WHERE "services".car_service_id IN (?, ?, ? ...);
If you remove N+1 your page will load faster but you should check that all your keys have indexes defined, too.
The above its just an example. You should really post your complete queries for us to understand exactly what you are doing. And those models Car and Truck, they look very similar, maybe it will be a better solution to use Single Table Inheritance .
You could rewrite this:
<% @trucks.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
<% end %>
as this
<% @trucks.each do |truck| %>
<%= content_tag :li, '...', data: {services: truck.services.map(&:name).join(', ')} %>
<% end %>
Hope this will help you somehow.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.