The 10th Round of ROR Lab.



Active Record
Query Interface(2)

        April 7th, 2012

       Hyoseong Choi
         ROR Lab.
Short Review


               ROR Lab.
Query Interface(1)
  - How to retrieve data from DB -
Bi-directional Associations
 :inverse_of
  class Customer < ActiveRecord::Base
    has_many :orders, :inverse_of => :customer
  end
   
  class Order < ActiveRecord::Base
    belongs_to :customer, :inverse_of => :orders
  end
  ---------------------------------------------------
  c = Customer.first
  o = c.orders.first
  c.first_name == o.customer.first_name # => true
  c.first_name = 'Manny'
  c.first_name == o.customer.first_name # => true




                                                    ROR Lab.
Overriding
          Conditions
                       - reorder -
 class Post < ActiveRecord::Base
   ..
   ..
   has_many :comments, :order => 'posted_at DESC'
 end
  



SELECT * FROM posts WHERE id = 10 ORDER BY name

SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC


                                                       ROR Lab.
Locking Records
    for Update
• To prevent “race conditions”
• To ensure “atomic updates”
• Two locking mechanisms
  ‣ Optimistic Locking : version control
  ‣ Pessimistic Locking : DB lock

                                       ROR Lab.
Optimistic Locking
• “lock_version” in DB table (default to 0)
• set_locking_column to change column name
  c1 = Client.find(1)
  c2 = Client.find(1)
   
  c1.first_name = "Michael"
  c1.save # increments the lock_version column
   
  c2.name = "should fail"




                                                 ROR Lab.
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • Multiple Associations
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Post.joins(:category, :comments)
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest                        SELECT posts.* FROM posts
end
 
                                          INNER JOIN categories
class Guest < ActiveRecord::Base           ON posts.category_id = categories.id
  belongs_to :comment                     INNER JOIN comments
end                                        ON comments.post_id = posts.id
 
class Tag < ActiveRecord::Base          “return all posts that have a category and at least one comment”
  belongs_to :post
end

                                                                                  ROR Lab.
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • Nested Associations(Single Level)
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Post.joins(:comments => :guest)
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest                        SELECT posts.* FROM posts
end
 
                                          INNER JOIN comments
class Guest < ActiveRecord::Base           ON comments.post_id = posts.id
  belongs_to :comment                     INNER JOIN guests
end                                        ON guests.comment_id = comments.id
 
class Tag < ActiveRecord::Base
  belongs_to :post
                                        “return all posts that have a comment made by a guest”
end

                                                                               ROR Lab.
Query Interface(2)
  - How to retrieve data from DB -
Eager Loading
     Associations
           : as few queries as possible

N + 1 queries problem


 clients = Client.limit(10)
  
 clients.each do |client|
   puts client.address.postcode




                                          ROR Lab.
Eager Loading
      Associations
             : as few queries as possible

Solution to N + 1 queries problem                     Only 2 queries

 clients = Client.includes(:address).limit(10)
  
 clients.each do |client|
   puts client.address.postcode



SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
  WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))

                                                              ROR Lab.
Eager Loading
      Associations
- Eager Loading Multiple Associations -
 Array of Multiple Associations




 Nested Associations Hash


 Category.includes(
  :posts => [{:comments => :guest}, :tags]).find(1)



                                                     ROR Lab.
Eager Loading
         Associations
- Conditions on Eager Loaded Associations -

      conditional “joins” > conditional “includes”


   Post.includes(:comments)
     .where("comments.visible", true)


  SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5
  FROM "posts"
  LEFT OUTER JOIN "comments"
  ON "comments"."post_id" = "posts"."id"
  WHERE (comments.visible = 1)


                                                                       ROR Lab.
Eager Loading
         Associations
- Conditions on Eager Loaded Associations -
                      INNER JOIN                             LEFT OUTER JOIN
      conditional “joins” > conditional “includes”


   Post.includes(:comments)
     .where("comments.visible", true)


  SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5
  FROM "posts"
  LEFT OUTER JOIN "comments"
  ON "comments"."post_id" = "posts"."id"
  WHERE (comments.visible = 1)


                                                                       ROR Lab.
Scopes

• To specify ARel queries
• Referenced as method calls on the
  association objects or model
• finder methods in a scope
• return an ActiveRecord::Relation object
                                       ROR Lab.
Scopes

class Post < ActiveRecord::Base
  scope :published, where(:published => true)
end
                              ARel query




                                                ROR Lab.
Scopes
chainable
class Post < ActiveRecord::Base
  scope :published,
  where(:published => true).joins(:category)
end




chainable within scopes
class Post < ActiveRecord::Base
  scope :published, where(:published => true)
  scope :published_and_commented,
published.and(self.arel_table[:comments_count].gt(0))




                                                        ROR Lab.
Scopes
                To call the scope

Post.published # => [published posts]

category = Category.first
category.posts.published
# => [published posts belonging to this category]




                                                    ROR Lab.
Scopes
                Working with times

class Post < ActiveRecord::Base
  scope :last_week,
  lambda { where("created_at < ?", Time.zone.now ) }
end     a lambda so that the scope is evaluated every time




                                                             ROR Lab.
Scopes
             Passing in arguments
class Post < ActiveRecord::Base
  scope :1_week_before,
  lambda { |time| where("created_at < ?", time) }
end




class Post < ActiveRecord::Base
  def self.1_week_before(time)
    where("created_at < ?", time)
  end
                      ***What about “as a class method” ?

                                                     ROR Lab.
Scopes
                   Passing in arguments
      class Post < ActiveRecord::Base
        scope :1_week_before,
        lambda { |time| where("created_at < ?", time) }
      end




           le
       eab
    erclass Post < ActiveRecord::Base
  ef
pr      def self.1_week_before(time)
          where("created_at < ?", time)
        end
                            ***What about “as a class method” ?

                                                           ROR Lab.
Scopes
Passing in arguments




                       ROR Lab.
Scopes
            Working with Scopes


client = Client.find_by_first_name("Ryan")




                                           ROR Lab.
Scopes
         Applying a default scope


class Client < ActiveRecord::Base
  default_scope where("removed_at IS NULL")




SELECT * FROM clients WHERE removed_at IS NULL



                                                 ROR Lab.
Scopes
   Removing all scopes




       Especially useful,
when escaping the default_scope



                                  ROR Lab.
Dynamic Finders
• Model.find_by_attribute_name
• Model.find_all_by_attribute_name
• Model.find_last_by_attribute_name
• Model.find_by_attribute_name!
• Model.find_by_attr1_and_attr2
Rails 3.1 < Argument Error on missing fields

                                      ROR Lab.
Find or build
      a new object

• first_or_create
• first_or_create!      Exception on invalid

• first_or_initialize

                                         ROR Lab.
Find or build
            a new object
• first_or_create
  Client.where(:first_name => 'Andy').first_or_create(:locked =>
  false)
  # => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at:
  "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">


  Client.find_or_create_by_first_name(:first_name =>
  "Andy", :locked => false)


SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES
('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
COMMIT

                                                                                        ROR Lab.
Find or build
          a new object
• first_or_create!
 class Client < ActiveRecord::Base
  validates :orders_count, :presence => true
 end

 Client.where(:first_name => 'Andy').first_or_create!(:locked =>
 false)
 # => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank




                                                                           ROR Lab.
Find or build
          a new object
• first_or_initialize
 nick = Client.where(:first_name =>
 'Nick').first_or_initialize(:locked => false)
 # => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false,
 created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
  
 SELECT * FROM clients WHERE (clients.first_name = 'Nick') LIMIT 1
 nick.persisted?
 # => false
 nick.new_record?
 # => true
 nick.save
 # => true


persisted? vs new_record?                                               ROR Lab.
Finding by SQL
 : return an array of objects
 where each object indicates a record

Client.find_by_sql("SELECT * FROM clients
  INNER JOIN orders ON clients.id = orders.client_id




cf. connection.select_all
       an array of hash

                                                       ROR Lab.
select_all
 : return an array of hash
 where each hash indicates a record

Client.connection.select_all("SELECT * FROM clients WHERE id =




cf. find_by_sql -> instantiates objects


                                                          ROR Lab.
pluck
 To query a single column from the underlying table of a model
 To return an array of values


Client.where(:active => true).pluck(:id)
# SELECT id FROM clients WHERE active = 1
 
Client.uniq.pluck(:role)




Client.select(:id).map { |c| c.id }

   Client.pluck(:id)



                                                         ROR Lab.
Existence of
           Objects
Client.exists?(1)

Client.exists?(1,2,3)
# or
Client.exists?([1,2,3])

Client.where(:first_name => 'Ryan').exists?




cf. find method
                                             ROR Lab.
Existence of
         Objects
# via a model
Post.any?
Post.many?
 
# via a named scope
Post.recent.any?
Post.recent.many?
 
# via a relation
Post.where(:published => true).any?
Post.where(:published => true).many?
 
# via an association
Post.first.categories.any?



                                       ROR Lab.
Calculations
 Client.count
 # SELECT count(*) AS count_all FROM clients

 Client.where(:first_name => 'Ryan').count
 # SELECT count(*) AS count_all FROM clients WHERE (first_name =
 'Ryan')

 Client.includes("orders").where(:first_name => 'Ryan', :orders



SELECT count(DISTINCT clients.id) AS count_all FROM clients
  LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE
  (clients.first_name = 'Ryan' AND orders.status = 'received')




                                                                 ROR Lab.
Calculations
Client.count(:age)

Client.average("orders_count")

Client.minimum("age")

Client.maximum("age")




                                 ROR Lab.
Running EXPLAIN
              a pretty printing that emulates the one of the database shells



              User.where(:id => 1).joins(:posts).explain




EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id`
=1
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |             |
|  1 | SIMPLE      | posts | ALL   | NULL          | NULL    | NULL    | NULL  |    1 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
2 rows in set (0.00 sec)



under MySQL

                                                                                                 ROR Lab.
Running EXPLAIN
               a pretty printing that emulates the one of the database shells



               User.where(:id => 1).joins(:posts).explain




EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1
                                  QUERY PLAN
------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=0.00..37.24 rows=8 width=0)
   Join Filter: (posts.user_id = users.id)
   ->  Index Scan using users_pkey on users  (cost=0.00..8.27 rows=1 width=4)
         Index Cond: (id = 1)
   ->  Seq Scan on posts  (cost=0.00..28.88 rows=8 width=4)
         Filter: (posts.user_id = 1)
(6 rows)



under PostgreSQL
                                                                                                        ROR Lab.
Running EXPLAIN
                                        Eager Loading


          User.where(:id => 1).includes(:posts).explain


 EXPLAIN for: SELECT `users`.* FROM `users`  WHERE `users`.`id` = 1
 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
 | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
 |  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
 1 row in set (0.00 sec)
  
 EXPLAIN for: SELECT `posts`.* FROM `posts`  WHERE `posts`.`user_id` IN (1)
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------
 +
 | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------
 +
 |  1 | SIMPLE      | posts | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where |



under MySQL                                                                        ROR Lab.
Running EXPLAIN
              Automatic EXPLAIN

To set the threshold in seconds
config.active_record.auto_explain_threshold_in_seconds



• disables automatic EXPLAIN => nil or no AR logger
• Development mode => default, 0.5 sec
• Test mode => nil
• Production mode => nil

                                                        ROR Lab.
Running EXPLAIN
      Disabling Automatic EXPLAIN

To be selectively silenced with
 ActiveRecord::Base.silence_auto_explain do
   # no automatic EXPLAIN is triggered here




• useful for queries are slow but fine
  : a heavyweight report of a admin interface


                                                ROR Lab.
감사합니다.

Active Record Query Interface (2), Season 1

  • 1.
    The 10th Roundof ROR Lab. Active Record Query Interface(2) April 7th, 2012 Hyoseong Choi ROR Lab.
  • 2.
    Short Review ROR Lab.
  • 3.
    Query Interface(1) - How to retrieve data from DB -
  • 4.
    Bi-directional Associations :inverse_of class Customer < ActiveRecord::Base   has_many :orders, :inverse_of => :customer end   class Order < ActiveRecord::Base   belongs_to :customer, :inverse_of => :orders end --------------------------------------------------- c = Customer.first o = c.orders.first c.first_name == o.customer.first_name # => true c.first_name = 'Manny' c.first_name == o.customer.first_name # => true ROR Lab.
  • 5.
    Overriding Conditions - reorder - class Post < ActiveRecord::Base   ..   ..   has_many :comments, :order => 'posted_at DESC' end   SELECT * FROM posts WHERE id = 10 ORDER BY name SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC ROR Lab.
  • 6.
    Locking Records for Update • To prevent “race conditions” • To ensure “atomic updates” • Two locking mechanisms ‣ Optimistic Locking : version control ‣ Pessimistic Locking : DB lock ROR Lab.
  • 7.
    Optimistic Locking • “lock_version”in DB table (default to 0) • set_locking_column to change column name c1 = Client.find(1) c2 = Client.find(1)   c1.first_name = "Michael" c1.save # increments the lock_version column   c2.name = "should fail" ROR Lab.
  • 8.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • Multiple Associations   belongs_to :category   has_many :comments   has_many :tags end Post.joins(:category, :comments)   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest SELECT posts.* FROM posts end     INNER JOIN categories class Guest < ActiveRecord::Base ON posts.category_id = categories.id   belongs_to :comment   INNER JOIN comments end ON comments.post_id = posts.id   class Tag < ActiveRecord::Base “return all posts that have a category and at least one comment”   belongs_to :post end ROR Lab.
  • 9.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • Nested Associations(Single Level)   belongs_to :category   has_many :comments   has_many :tags end Post.joins(:comments => :guest)   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest SELECT posts.* FROM posts end     INNER JOIN comments class Guest < ActiveRecord::Base ON comments.post_id = posts.id   belongs_to :comment   INNER JOIN guests end ON guests.comment_id = comments.id   class Tag < ActiveRecord::Base   belongs_to :post “return all posts that have a comment made by a guest” end ROR Lab.
  • 10.
    Query Interface(2) - How to retrieve data from DB -
  • 11.
    Eager Loading Associations : as few queries as possible N + 1 queries problem clients = Client.limit(10)   clients.each do |client|   puts client.address.postcode ROR Lab.
  • 12.
    Eager Loading Associations : as few queries as possible Solution to N + 1 queries problem Only 2 queries clients = Client.includes(:address).limit(10)   clients.each do |client|   puts client.address.postcode SELECT * FROM clients LIMIT 10 SELECT addresses.* FROM addresses   WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10)) ROR Lab.
  • 13.
    Eager Loading Associations - Eager Loading Multiple Associations - Array of Multiple Associations Nested Associations Hash Category.includes( :posts => [{:comments => :guest}, :tags]).find(1) ROR Lab.
  • 14.
    Eager Loading Associations - Conditions on Eager Loaded Associations - conditional “joins” > conditional “includes” Post.includes(:comments) .where("comments.visible", true) SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1) ROR Lab.
  • 15.
    Eager Loading Associations - Conditions on Eager Loaded Associations - INNER JOIN LEFT OUTER JOIN conditional “joins” > conditional “includes” Post.includes(:comments) .where("comments.visible", true) SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1) ROR Lab.
  • 16.
    Scopes • To specifyARel queries • Referenced as method calls on the association objects or model • finder methods in a scope • return an ActiveRecord::Relation object ROR Lab.
  • 17.
    Scopes class Post <ActiveRecord::Base   scope :published, where(:published => true) end ARel query ROR Lab.
  • 18.
    Scopes chainable class Post <ActiveRecord::Base   scope :published, where(:published => true).joins(:category) end chainable within scopes class Post < ActiveRecord::Base   scope :published, where(:published => true)   scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0)) ROR Lab.
  • 19.
    Scopes To call the scope Post.published # => [published posts] category = Category.first category.posts.published # => [published posts belonging to this category] ROR Lab.
  • 20.
    Scopes Working with times class Post < ActiveRecord::Base   scope :last_week, lambda { where("created_at < ?", Time.zone.now ) } end a lambda so that the scope is evaluated every time ROR Lab.
  • 21.
    Scopes Passing in arguments class Post < ActiveRecord::Base   scope :1_week_before, lambda { |time| where("created_at < ?", time) } end class Post < ActiveRecord::Base   def self.1_week_before(time)     where("created_at < ?", time)   end ***What about “as a class method” ? ROR Lab.
  • 22.
    Scopes Passing in arguments class Post < ActiveRecord::Base   scope :1_week_before, lambda { |time| where("created_at < ?", time) } end le eab erclass Post < ActiveRecord::Base ef pr   def self.1_week_before(time)     where("created_at < ?", time)   end ***What about “as a class method” ? ROR Lab.
  • 23.
  • 24.
    Scopes Working with Scopes client = Client.find_by_first_name("Ryan") ROR Lab.
  • 25.
    Scopes Applying a default scope class Client < ActiveRecord::Base   default_scope where("removed_at IS NULL") SELECT * FROM clients WHERE removed_at IS NULL ROR Lab.
  • 26.
    Scopes Removing all scopes Especially useful, when escaping the default_scope ROR Lab.
  • 27.
    Dynamic Finders • Model.find_by_attribute_name •Model.find_all_by_attribute_name • Model.find_last_by_attribute_name • Model.find_by_attribute_name! • Model.find_by_attr1_and_attr2 Rails 3.1 < Argument Error on missing fields ROR Lab.
  • 28.
    Find or build a new object • first_or_create • first_or_create! Exception on invalid • first_or_initialize ROR Lab.
  • 29.
    Find or build a new object • first_or_create Client.where(:first_name => 'Andy').first_or_create(:locked => false) # => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27"> Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false) SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1 BEGIN INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57') COMMIT ROR Lab.
  • 30.
    Find or build a new object • first_or_create! class Client < ActiveRecord::Base validates :orders_count, :presence => true end Client.where(:first_name => 'Andy').first_or_create!(:locked => false) # => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank ROR Lab.
  • 31.
    Find or build a new object • first_or_initialize nick = Client.where(:first_name => 'Nick').first_or_initialize(:locked => false) # => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">   SELECT * FROM clients WHERE (clients.first_name = 'Nick') LIMIT 1 nick.persisted? # => false nick.new_record? # => true nick.save # => true persisted? vs new_record? ROR Lab.
  • 32.
    Finding by SQL : return an array of objects where each object indicates a record Client.find_by_sql("SELECT * FROM clients   INNER JOIN orders ON clients.id = orders.client_id cf. connection.select_all an array of hash ROR Lab.
  • 33.
    select_all : returnan array of hash where each hash indicates a record Client.connection.select_all("SELECT * FROM clients WHERE id = cf. find_by_sql -> instantiates objects ROR Lab.
  • 34.
    pluck To querya single column from the underlying table of a model To return an array of values Client.where(:active => true).pluck(:id) # SELECT id FROM clients WHERE active = 1   Client.uniq.pluck(:role) Client.select(:id).map { |c| c.id } Client.pluck(:id) ROR Lab.
  • 35.
    Existence of Objects Client.exists?(1) Client.exists?(1,2,3) # or Client.exists?([1,2,3]) Client.where(:first_name => 'Ryan').exists? cf. find method ROR Lab.
  • 36.
    Existence of Objects # via a model Post.any? Post.many?   # via a named scope Post.recent.any? Post.recent.many?   # via a relation Post.where(:published => true).any? Post.where(:published => true).many?   # via an association Post.first.categories.any? ROR Lab.
  • 37.
    Calculations Client.count #SELECT count(*) AS count_all FROM clients Client.where(:first_name => 'Ryan').count # SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan') Client.includes("orders").where(:first_name => 'Ryan', :orders SELECT count(DISTINCT clients.id) AS count_all FROM clients   LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE   (clients.first_name = 'Ryan' AND orders.status = 'received') ROR Lab.
  • 38.
  • 39.
    Running EXPLAIN a pretty printing that emulates the one of the database shells User.where(:id => 1).joins(:posts).explain EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` =1 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra       | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ |  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |             | |  1 | SIMPLE      | posts | ALL   | NULL          | NULL    | NULL    | NULL  |    1 | Using where | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ 2 rows in set (0.00 sec) under MySQL ROR Lab.
  • 40.
    Running EXPLAIN a pretty printing that emulates the one of the database shells User.where(:id => 1).joins(:posts).explain EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1                                   QUERY PLAN ------------------------------------------------------------------------------  Nested Loop Left Join  (cost=0.00..37.24 rows=8 width=0)    Join Filter: (posts.user_id = users.id)    ->  Index Scan using users_pkey on users  (cost=0.00..8.27 rows=1 width=4)          Index Cond: (id = 1)    ->  Seq Scan on posts  (cost=0.00..28.88 rows=8 width=4)          Filter: (posts.user_id = 1) (6 rows) under PostgreSQL ROR Lab.
  • 41.
    Running EXPLAIN Eager Loading User.where(:id => 1).includes(:posts).explain EXPLAIN for: SELECT `users`.* FROM `users`  WHERE `users`.`id` = 1 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ |  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 1 row in set (0.00 sec)   EXPLAIN for: SELECT `posts`.* FROM `posts`  WHERE `posts`.`user_id` IN (1) +----+-------------+-------+------+---------------+------+---------+------+------+------------- + | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       | +----+-------------+-------+------+---------------+------+---------+------+------+------------- + |  1 | SIMPLE      | posts | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where | under MySQL ROR Lab.
  • 42.
    Running EXPLAIN Automatic EXPLAIN To set the threshold in seconds config.active_record.auto_explain_threshold_in_seconds • disables automatic EXPLAIN => nil or no AR logger • Development mode => default, 0.5 sec • Test mode => nil • Production mode => nil ROR Lab.
  • 43.
    Running EXPLAIN Disabling Automatic EXPLAIN To be selectively silenced with ActiveRecord::Base.silence_auto_explain do   # no automatic EXPLAIN is triggered here • useful for queries are slow but fine : a heavyweight report of a admin interface ROR Lab.
  • 44.