SlideShare a Scribd company logo
Ror lab. season 2
     - the 11th round -


   Active Record
Query Interface (2)

     December 1st, 2012

       Hyoseong Choi
Eager Loading
     Associations
          : as few queries as possible

N + 1 queries problem


 clients = Client.limit(10)
  
 clients.each do |client|
   puts client.address.postcode
 end
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
 end


SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
  WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
Eager Loading
      Associations
- Eager Loading Multiple Associations -
 Array of Multiple Associations


 Post.includes(:category, :comments)



 Nested Associations Hash


 Category.includes(
   :posts => [{:comments => :guest}, :tags]).find(1)
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)
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)
Scopes

• ToSQL AST(Abstract Syntax Tree) manger for Ruby
  :a
     specify ARel queries

• Referenced as method calls on the
  association objects or model
• finder methods in a scope
• return an ActiveRecord::Relation object
Scopes

class Post < ActiveRecord::Base
  scope :published, where(:published => true)
end
                        ARel query
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))
end
Scopes
             To call the scope

Post.published # => [published posts]

category = Category.first
category.posts.published
# => [published posts belonging to this category]
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
Scopes
          Passing in arguments
class Post < ActiveRecord::Base
  scope :1_week_before,
  lambda { |time| where("created_at < ?", time) }
end

Post.1_week_before(Time.zone.now)




class Post < ActiveRecord::Base
  def self.1_week_before(time)
    where("created_at < ?", time)
  end
end              ***What about “as   a class method” ?
Scopes
                          Passing in arguments
            class Post < ActiveRecord::Base
              scope :1_week_before,
              lambda { |time| where("created_at < ?", time) }
            end

            Post.1_week_before(Time.zone.now)


                   b le
            r ea
     e fe   class Post < ActiveRecord::Base
pr            def self.1_week_before(time)
                where("created_at < ?", time)
              end
            end              ***What about “as   a class method” ?
Scopes
          Passing in arguments


category.posts.1_week_before(time)
Scopes
          Working with Scopes


client = Client.find_by_first_name("Ryan")
orders = client.orders.scoped




orders.where("created_at > ?", 30.days.ago)
Scopes
        Applying a default scope


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



SELECT * FROM clients WHERE removed_at IS NULL
Scopes
          Removing all scopes


Client.unscoped.all




             Especially useful,
      when escaping the default_scope
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.2 => Argument Error on missing fields
Find or build
      a new object

• first_or_create
• first_or_create!      Exception on invalid

• first_or_initialize
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
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
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?
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
  ORDER clients.created_at desc")



cf. connection.select_all
       an array of hash
select_all
 : return an array of hash
 where each hash indicates a record

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




cf. find_by_sql => instantiates objects
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)
# SELECT DISTINCT role FROM clients




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

    Client.pluck(:id)
Existence of
        Objects
Client.exists?(1)

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

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

Client.exists?



cf. find method
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?
Post.first.categories.many?
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 => {:status => 'received'}).count



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')
Calculations
Client.count(:age)

Client.average("orders_count")

Client.minimum("age")

Client.maximum("age")

Client.sum("orders_count")
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
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
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 |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 1 row in set (0.00 sec)



under MySQL
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
Running EXPLAIN
     Disabling Automatic EXPLAIN

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



• useful for queries are slow but fine
  : a heavyweight report of a admin interface
감사합니다.

More Related Content

PPTX
Typed? Dynamic? Both! Cross-platform DSLs in C#
ZIP
Barcamp Auckland Rails3 presentation
PPTX
Database security
PDF
ActiveRecord Query Interface (1), Season 1
PDF
PDF
Painless Persistence with Realm
PPT
Micro-ORM Introduction - Don't overcomplicate
PDF
Scala active record
Typed? Dynamic? Both! Cross-platform DSLs in C#
Barcamp Auckland Rails3 presentation
Database security
ActiveRecord Query Interface (1), Season 1
Painless Persistence with Realm
Micro-ORM Introduction - Don't overcomplicate
Scala active record

What's hot (20)

PDF
Scala ActiveRecord
KEY
Getting the most out of Java [Nordic Coding-2010]
PDF
Http4s, Doobie and Circe: The Functional Web Stack
DOCX
descriptive programming
PDF
Alternate JVM Languages
PPTX
Python: Basic Inheritance
PDF
Nativescript angular
PDF
Clean up your code with C#6
PPTX
What’s new in C# 6
PDF
Object Oriented Exploitation: New techniques in Windows mitigation bypass
PPTX
Python and EM CLI: The Enterprise Management Super Tools
PDF
PPTX
JavaFX 2.0 With Alternative Languages - JavaOne 2011
PDF
The Ring programming language version 1.5.4 book - Part 38 of 185
PDF
Activator and Reactive at Play NYC meetup
PDF
The Ring programming language version 1.2 book - Part 27 of 84
KEY
RIAs Done Right: Grails, Flex, and EXT GWT
PPTX
Slick: Bringing Scala’s Powerful Features to Your Database Access
PDF
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
PDF
Restful App Engine
Scala ActiveRecord
Getting the most out of Java [Nordic Coding-2010]
Http4s, Doobie and Circe: The Functional Web Stack
descriptive programming
Alternate JVM Languages
Python: Basic Inheritance
Nativescript angular
Clean up your code with C#6
What’s new in C# 6
Object Oriented Exploitation: New techniques in Windows mitigation bypass
Python and EM CLI: The Enterprise Management Super Tools
JavaFX 2.0 With Alternative Languages - JavaOne 2011
The Ring programming language version 1.5.4 book - Part 38 of 185
Activator and Reactive at Play NYC meetup
The Ring programming language version 1.2 book - Part 27 of 84
RIAs Done Right: Grails, Flex, and EXT GWT
Slick: Bringing Scala’s Powerful Features to Your Database Access
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Restful App Engine
Ad

Similar to ActiveRecord Query Interface (2), Season 2 (20)

KEY
Active Record Query Interface (2), Season 1
KEY
Active Record Query Interface (1), Season 2
PDF
科特林λ學
KEY
Ruby/Rails
PDF
Timothy N. Tsvetkov, Rails 3.1
PDF
Scala @ TechMeetup Edinburgh
PPTX
Intro to Rails 4
PDF
Антипаттерны модульного тестирования
PPTX
Taxonomy of Scala
PPTX
Кирилл Безпалый, .NET Developer, Ciklum
KEY
Why ruby
ODP
Ruby on rails
ZIP
Rails 3 (beta) Roundup
PDF
The Ring programming language version 1.5.2 book - Part 37 of 181
PDF
Spring Day | Spring and Scala | Eberhard Wolff
PDF
YiiConf 2012 - Alexander Makarov - Yii2, what's new
PDF
KSP intro
PPS
CS101- Introduction to Computing- Lecture 29
PDF
Introduction to Active Record - Silicon Valley Ruby Conference 2007
PPTX
Smarter Testing With Spock
Active Record Query Interface (2), Season 1
Active Record Query Interface (1), Season 2
科特林λ學
Ruby/Rails
Timothy N. Tsvetkov, Rails 3.1
Scala @ TechMeetup Edinburgh
Intro to Rails 4
Антипаттерны модульного тестирования
Taxonomy of Scala
Кирилл Безпалый, .NET Developer, Ciklum
Why ruby
Ruby on rails
Rails 3 (beta) Roundup
The Ring programming language version 1.5.2 book - Part 37 of 181
Spring Day | Spring and Scala | Eberhard Wolff
YiiConf 2012 - Alexander Makarov - Yii2, what's new
KSP intro
CS101- Introduction to Computing- Lecture 29
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Smarter Testing With Spock
Ad

More from RORLAB (20)

PDF
Getting Started with Rails (4)
PDF
Getting Started with Rails (3)
PDF
Getting Started with Rails (2)
PDF
Getting Started with Rails (1)
PDF
Self join in active record association
PDF
Asset Pipeline in Ruby on Rails
PDF
레일스가이드 한글번역 공개프로젝트 RORLabGuides 소개
PDF
Active Support Core Extension (3)
PDF
Active Support Core Extension (2)
PDF
Active Support Core Extensions (1)
PDF
Action Controller Overview, Season 2
PDF
Action View Form Helpers - 2, Season 2
PDF
Action View Form Helpers - 1, Season 2
PDF
Layouts and Rendering in Rails, Season 2
KEY
Active Record Association (2), Season 2
KEY
ActiveRecord Association (1), Season 2
KEY
ActiveRecord Callbacks & Observers, Season 2
KEY
ActiveRecord Validations, Season 2
KEY
Rails Database Migration, Season 2
KEY
Getting started with Rails (4), Season 2
Getting Started with Rails (4)
Getting Started with Rails (3)
Getting Started with Rails (2)
Getting Started with Rails (1)
Self join in active record association
Asset Pipeline in Ruby on Rails
레일스가이드 한글번역 공개프로젝트 RORLabGuides 소개
Active Support Core Extension (3)
Active Support Core Extension (2)
Active Support Core Extensions (1)
Action Controller Overview, Season 2
Action View Form Helpers - 2, Season 2
Action View Form Helpers - 1, Season 2
Layouts and Rendering in Rails, Season 2
Active Record Association (2), Season 2
ActiveRecord Association (1), Season 2
ActiveRecord Callbacks & Observers, Season 2
ActiveRecord Validations, Season 2
Rails Database Migration, Season 2
Getting started with Rails (4), Season 2

Recently uploaded (20)

PPTX
Final Presentation General Medicine 03-08-2024.pptx
PDF
Updated Idioms and Phrasal Verbs in English subject
PDF
LNK 2025 (2).pdf MWEHEHEHEHEHEHEHEHEHEHE
PDF
A systematic review of self-coping strategies used by university students to ...
PDF
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
PDF
01-Introduction-to-Information-Management.pdf
PPTX
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
PDF
ChatGPT for Dummies - Pam Baker Ccesa007.pdf
PDF
Supply Chain Operations Speaking Notes -ICLT Program
PPTX
Cell Types and Its function , kingdom of life
PPTX
History, Philosophy and sociology of education (1).pptx
PPTX
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
PDF
LDMMIA Reiki Yoga Finals Review Spring Summer
PDF
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
PDF
What if we spent less time fighting change, and more time building what’s rig...
PDF
Trump Administration's workforce development strategy
PDF
Yogi Goddess Pres Conference Studio Updates
PPTX
Orientation - ARALprogram of Deped to the Parents.pptx
PDF
Classroom Observation Tools for Teachers
PDF
Practical Manual AGRO-233 Principles and Practices of Natural Farming
Final Presentation General Medicine 03-08-2024.pptx
Updated Idioms and Phrasal Verbs in English subject
LNK 2025 (2).pdf MWEHEHEHEHEHEHEHEHEHEHE
A systematic review of self-coping strategies used by university students to ...
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
01-Introduction-to-Information-Management.pdf
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
ChatGPT for Dummies - Pam Baker Ccesa007.pdf
Supply Chain Operations Speaking Notes -ICLT Program
Cell Types and Its function , kingdom of life
History, Philosophy and sociology of education (1).pptx
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
LDMMIA Reiki Yoga Finals Review Spring Summer
OBE - B.A.(HON'S) IN INTERIOR ARCHITECTURE -Ar.MOHIUDDIN.pdf
What if we spent less time fighting change, and more time building what’s rig...
Trump Administration's workforce development strategy
Yogi Goddess Pres Conference Studio Updates
Orientation - ARALprogram of Deped to the Parents.pptx
Classroom Observation Tools for Teachers
Practical Manual AGRO-233 Principles and Practices of Natural Farming

ActiveRecord Query Interface (2), Season 2

  • 1. Ror lab. season 2 - the 11th round - Active Record Query Interface (2) December 1st, 2012 Hyoseong Choi
  • 2. Eager Loading Associations : as few queries as possible N + 1 queries problem clients = Client.limit(10)   clients.each do |client|   puts client.address.postcode end
  • 3. 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 end SELECT * FROM clients LIMIT 10 SELECT addresses.* FROM addresses   WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
  • 4. Eager Loading Associations - Eager Loading Multiple Associations - Array of Multiple Associations Post.includes(:category, :comments) Nested Associations Hash Category.includes( :posts => [{:comments => :guest}, :tags]).find(1)
  • 5. 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)
  • 6. 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)
  • 7. Scopes • ToSQL AST(Abstract Syntax Tree) manger for Ruby :a specify ARel queries • Referenced as method calls on the association objects or model • finder methods in a scope • return an ActiveRecord::Relation object
  • 8. Scopes class Post < ActiveRecord::Base   scope :published, where(:published => true) end ARel query
  • 9. 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)) end
  • 10. Scopes To call the scope Post.published # => [published posts] category = Category.first category.posts.published # => [published posts belonging to this category]
  • 11. 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
  • 12. Scopes Passing in arguments class Post < ActiveRecord::Base   scope :1_week_before, lambda { |time| where("created_at < ?", time) } end Post.1_week_before(Time.zone.now) class Post < ActiveRecord::Base   def self.1_week_before(time)     where("created_at < ?", time)   end end ***What about “as a class method” ?
  • 13. Scopes Passing in arguments class Post < ActiveRecord::Base   scope :1_week_before, lambda { |time| where("created_at < ?", time) } end Post.1_week_before(Time.zone.now) b le r ea e fe class Post < ActiveRecord::Base pr   def self.1_week_before(time)     where("created_at < ?", time)   end end ***What about “as a class method” ?
  • 14. Scopes Passing in arguments category.posts.1_week_before(time)
  • 15. Scopes Working with Scopes client = Client.find_by_first_name("Ryan") orders = client.orders.scoped orders.where("created_at > ?", 30.days.ago)
  • 16. Scopes Applying a default scope class Client < ActiveRecord::Base   default_scope where("removed_at IS NULL") end SELECT * FROM clients WHERE removed_at IS NULL
  • 17. Scopes Removing all scopes Client.unscoped.all Especially useful, when escaping the default_scope
  • 18. 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.2 => Argument Error on missing fields
  • 19. Find or build a new object • first_or_create • first_or_create! Exception on invalid • first_or_initialize
  • 20. 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
  • 21. 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
  • 22. 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?
  • 23. 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   ORDER clients.created_at desc") cf. connection.select_all an array of hash
  • 24. select_all : return an array of hash where each hash indicates a record Client.connection.select_all("SELECT * FROM clients WHERE id = '1'") cf. find_by_sql => instantiates objects
  • 25. 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) # SELECT DISTINCT role FROM clients Client.select(:id).map { |c| c.id } Client.pluck(:id)
  • 26. Existence of Objects Client.exists?(1) Client.exists?(1,2,3) # or Client.exists?([1,2,3]) Client.where(:first_name => 'Ryan').exists? Client.exists? cf. find method
  • 27. 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? Post.first.categories.many?
  • 28. 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 => {:status => 'received'}).count 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')
  • 30. 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
  • 31. 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
  • 32. 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 | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) under MySQL
  • 33. 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
  • 34. Running EXPLAIN Disabling Automatic EXPLAIN To be selectively silenced with ActiveRecord::Base.silence_auto_explain do   # no automatic EXPLAIN is triggered here end • useful for queries are slow but fine : a heavyweight report of a admin interface
  • 36.