SlideShare a Scribd company logo
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.
감사합니다.

More Related Content

KEY
Routing 1, Season 1
PDF
Layouts and Rendering in Rails, Season 2
PDF
Action Controller Overview, Season 2
PDF
RSpec 3.0: Under the Covers
KEY
Action Controller Overview, Season 1
PDF
Rails 3 overview
PPTX
REST APIs in Laravel 101
PDF
Metadata-driven Testing
Routing 1, Season 1
Layouts and Rendering in Rails, Season 2
Action Controller Overview, Season 2
RSpec 3.0: Under the Covers
Action Controller Overview, Season 1
Rails 3 overview
REST APIs in Laravel 101
Metadata-driven Testing

What's hot (20)

PDF
Rails 3 Beautiful Code
PDF
Findbin libs
PDF
Bootstrat REST APIs with Laravel 5
PDF
CQ5 QueryBuilder - .adaptTo(Berlin) 2011
PDF
XamarinとAWSをつないでみた話
PDF
Rails 4.0
PDF
iOS Behavior-Driven Development
PDF
Rails and the Apache SOLR Search Engine
PDF
Getting Started-with-Laravel
PDF
Construire son JDK en 10 étapes
KEY
Php Unit With Zend Framework Zendcon09
PDF
Quality Assurance for PHP projects - ZendCon 2012
KEY
Supa fast Ruby + Rails
PDF
Laravel dokumentacja Restful API - swagger
PDF
Perl6 Regexen: Reduce the line noise in your code.
PPTX
Designing CakePHP plugins for consuming APIs
PPTX
Service approach for development REST API in Symfony2
PDF
Arquillian Constellation
PDF
Fast Web Applications Development with Ruby on Rails on Oracle
PPTX
Zend framework
Rails 3 Beautiful Code
Findbin libs
Bootstrat REST APIs with Laravel 5
CQ5 QueryBuilder - .adaptTo(Berlin) 2011
XamarinとAWSをつないでみた話
Rails 4.0
iOS Behavior-Driven Development
Rails and the Apache SOLR Search Engine
Getting Started-with-Laravel
Construire son JDK en 10 étapes
Php Unit With Zend Framework Zendcon09
Quality Assurance for PHP projects - ZendCon 2012
Supa fast Ruby + Rails
Laravel dokumentacja Restful API - swagger
Perl6 Regexen: Reduce the line noise in your code.
Designing CakePHP plugins for consuming APIs
Service approach for development REST API in Symfony2
Arquillian Constellation
Fast Web Applications Development with Ruby on Rails on Oracle
Zend framework
Ad

Similar to Active Record Query Interface (2), Season 1 (20)

PDF
ActiveRecord Query Interface (2), Season 2
PDF
ActiveRecord Query Interface (1), Season 1
KEY
Active Record Query Interface (1), Season 2
ZIP
Rails 3 (beta) Roundup
KEY
Ruby/Rails
PDF
Rails on Oracle 2011
PPTX
Intro to Rails 4
PDF
Pourquoi ruby et rails déchirent
KEY
Active Record Form Helpers, Season 1
PDF
Introduction to Active Record - Silicon Valley Ruby Conference 2007
PDF
Ruby on Rails ステップアップ講座 - 大場寧子
PDF
Ruby on Rails 2.1 What's New
PDF
Introduction to Active Record at MySQL Conference 2007
PDF
Ruby on Rails 中級者を目指して - 大場寧子
PDF
Timothy N. Tsvetkov, Rails 3.1
PDF
Carlosbrando Rubyonrails21 En
ZIP
Barcamp Auckland Rails3 presentation
PPTX
Кирилл Безпалый, .NET Developer, Ciklum
PDF
KSP intro
PPTX
Java Libraries You Can’t Afford to Miss
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (1), Season 1
Active Record Query Interface (1), Season 2
Rails 3 (beta) Roundup
Ruby/Rails
Rails on Oracle 2011
Intro to Rails 4
Pourquoi ruby et rails déchirent
Active Record Form Helpers, Season 1
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Ruby on Rails ステップアップ講座 - 大場寧子
Ruby on Rails 2.1 What's New
Introduction to Active Record at MySQL Conference 2007
Ruby on Rails 中級者を目指して - 大場寧子
Timothy N. Tsvetkov, Rails 3.1
Carlosbrando Rubyonrails21 En
Barcamp Auckland Rails3 presentation
Кирилл Безпалый, .NET Developer, Ciklum
KSP intro
Java Libraries You Can’t Afford to Miss
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 View Form Helpers - 2, Season 2
PDF
Action View Form Helpers - 1, 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
KEY
Getting started with Rails (3), Season 2
KEY
Getting started with Rails (2), 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 View Form Helpers - 2, Season 2
Action View Form Helpers - 1, 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
Getting started with Rails (3), Season 2
Getting started with Rails (2), Season 2

Recently uploaded (20)

PPTX
Cloud computing and distributed systems.
PDF
Modernizing your data center with Dell and AMD
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Encapsulation theory and applications.pdf
PDF
NewMind AI Monthly Chronicles - July 2025
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
A Presentation on Artificial Intelligence
PDF
Machine learning based COVID-19 study performance prediction
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Cloud computing and distributed systems.
Modernizing your data center with Dell and AMD
Understanding_Digital_Forensics_Presentation.pptx
Spectral efficient network and resource selection model in 5G networks
Encapsulation theory and applications.pdf
NewMind AI Monthly Chronicles - July 2025
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
A Presentation on Artificial Intelligence
Machine learning based COVID-19 study performance prediction
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Unlocking AI with Model Context Protocol (MCP)
Per capita expenditure prediction using model stacking based on satellite ima...
Review of recent advances in non-invasive hemoglobin estimation
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication

Active Record Query Interface (2), Season 1

  • 1. The 10th Round of 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 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.
  • 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.
  • 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 : 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.
  • 34. 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.
  • 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.
  • 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.
  • 45.   ROR Lab.

Editor's Notes