Monday, October 10, 2016

The "Includes" Magic in Rails

The "Includes" Magic in Rails
If there are many associated models needed to be retrieved during a query, it’s time to use ‘includes’ instead of nothing. It will increase your apps performance by reducing the number of queries to database.
Here is the scenario:
Order has_many Orderdetails

Without ‘includes’

The code:
@orderdetails = @order.orderdetails
It generates the log below.
Started GET “/orders/1” for ::1 at 2016-10-10 04:09:07 +0800
Processing by OrdersController#show as HTML
Parameters: {“id”=>”1”}
Order Load (0.3ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Rendering orders/show.html.erb within layouts/application
Orderdetail Load (0.6ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Model Load (0.4ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.4ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.6ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Color Load (0.2ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Size Load (0.1ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 4], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
CACHE (0.0ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” = ? ORDER BY name ASC LIMIT ? [[“id”, 2], [“LIMIT”, 1]]
Rendered orders/show.html.erb within layouts/application (105.4ms)
Rendered common/_navbar.html.erb (1.1ms)
Completed 200 OK in 275ms (Views: 236.7ms | ActiveRecord: 5.1ms)
It takes 14 queries and 5.1ms.

With ‘includes’

The code:
@orderdetails = @order.orderdetails.includes(:model, :size, :color)
It generates the log below.
Started GET “/orders/1” for ::1 at 2016-10-10 14:26:43 +0800
ActiveRecord::SchemaMigration Load (0.2ms) SELECT “schema_migrations”.* FROM “schema_migrations”
Processing by OrdersController#show as HTML
Parameters: {“id”=>”1”}
Order Load (0.2ms) SELECT “orders”.* FROM “orders” WHERE “orders”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
Rendering orders/show.html.erb within layouts/application
Orderdetail Load (0.2ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Model Load (0.1ms) SELECT “models”.* FROM “models” WHERE “models”.”id” = 1
Size Load (0.2ms) SELECT “sizes”.* FROM “sizes” WHERE “sizes”.”id” IN (1, 2) ORDER BY name ASC
Color Load (0.2ms) SELECT “colors”.* FROM “colors” WHERE “colors”.”id” IN (4, 2) ORDER BY name ASC
Orderdetail Load (0.2ms) SELECT “orderdetails”.* FROM “orderdetails” WHERE “orderdetails”.”order_id” = ? ORDER BY model_id, size_id ASC [[“order_id”, 1]]
Rendered orders/show.html.erb within layouts/application (84.2ms)
Rendered common/_navbar.html.erb (1.9ms)
Completed 200 OK in 617ms (Views: 560.0ms | ActiveRecord: 3.4ms)
It takes 6 queries and 3.4ms.
The includes method can minimize the queries to the database. Rails generates a single query each time it need the associated model by default. The ‘includes’ method, however, aggregates the queries to the same associated model .
For example, If there are four order details belong to order id ‘1’, Rails generates 12 ( 4 records x 3 associated models ) queries by default. On the other hand, it only takes 3 queries by using the ‘includes’ method.

Conclusion

Add ‘includes’ method for the query with many associated models.

No comments:

Post a Comment