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


   Active Record
Query Interface (1)

     November 17th, 2012

       Hyoseong Choi
ActiveRecord
• No more SQL statements      MySQL
                              PostgreSQL
  : select * from tables      SQLite
                              ...
                                   ORM
Action Sequences
                                  Active
    1. SQL query                  Record

    2. Result set
                                  Finder
    3. Ruby object               Methods
    4. (after_find callback)
Finder Methods of
  ActiveRecord
1.where
2.select
3.group
4.order
5.reorder
6.reverse_order
7.limit
8.offset
9.joins
10.includes
11.lock
12.readonly
13.from
14.having

 ActiveRecord::Relation
Retrieving
    A Single Object
• find :ActiveRecord::RecordNotFound
• first :nil
• last :nil
• first! : ActiveRecord::RecordNotFound
• last! : ActiveRecord::RecordNotFound
Retrieving
    A Single Object
                            - find -


  # Find the client with primary key (id) 10.

  client = Client.find(10)




SELECT * FROM clients WHERE (clients.id = 10)

ActiveRecord::RecordNotFound exception
Retrieving
    A Single Object
                            - first -


  client = Client.first

  # => #<Client id: 1, first_name: "Lifo">



SELECT * FROM clients LIMIT 1


nil if no matching record is found
Retrieving
    A Single Object
                             - last -


  client = Client.last

  # => #<Client id: 221, first_name: "Russel">



SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1


nil if no matching record is found
Retrieving
    A Single Object
                          - first! -


  client = Client.first!

  # => #<Client id: 1, first_name: "Lifo">



SELECT * FROM clients LIMIT 1


RecordNotFound if no matching record
Retrieving
    A Single Object
                           - last! -


  client = Client.last!

  # => #<Client id: 221, first_name: "Russel">



SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1


RecordNotFound if no matching record
Retrieving
  Multiple Objects
• Using multiple primary keys
• In batches
 • find_each :batch_size, :start,
 • find_in_batches :batch_size, :start
    + find options (except for :order, :limit)
Retrieving
  Multiple Objects
          - Using multiple primary keys -

  # Find the clients with primary keys 1 and 10.
  client = Client.find([1, 10])
  # Or even Client.find(1, 10)
  # => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10,




SELECT * FROM clients WHERE (clients.id IN (1,10))

ActiveRecord::RecordNotFound exception
Retrieving
       Multiple Objects
                            - in Batches -
                  to iterate over a large set of records

• find_each : each record to the block individually as a model
• find_in_batches : the entire batch to the block as an array of models
       # This is very inefficient when the users table has thousands
       of rows.
       User.all.each do |user|
         NewsLetter.weekly_deliver(user)

                                                OK to 1,000
Retrieving
Multiple Objects
             - in Batches : find_each -

User.find_each do |user|
  NewsLetter.weekly_deliver(user)
end

User.find_each(:batch_size => 5000) do |user|
  NewsLetter.weekly_deliver(user)
end

User.find_each(:start => 2000, :batch_size => 5000) do |
user|
  NewsLetter.weekly_deliver(user)
Retrieving
   Multiple Objects
                - in Batches : find_each -

  Article.find_each { |a| ... } # => iterate over all articles, in
  chunks of 1000 (the default)

  Article.find_each(:conditions => { :published =>
  true }, :batch_size => 100 ) { |a| ... }
    # iterate over published articles in chunks of 100




http://archives.ryandaigle.com/articles/2009/2/23/what-s-
new-in-edge-rails-batched-find
Retrieving
     Multiple Objects
              - in Batches : find_in_batches -

    # Give add_invoices an array of 1000 invoices at a time
    Invoice.find_in_batches(:include => :invoice_lines) do |
    invoices|
      export.add_invoices(invoices)
    end



options :
  • :batch_size & :start
  • options of find method (except :order and :limit)
Retrieving
   Multiple Objects
              - in Batches : find_in_batches -

Article.find_in_batches { |articles| articles.each { |a| ... } } # => articles
is array of size 1000
Article.find_in_batches(:batch_size => 100 ) { |articles| articles.each { |
a| ... } }
  # iterate over all articles in chunks of 100

class Article < ActiveRecord::Base
 scope :published, :conditions => { :published => true }
end

Article.published.find_in_batches(:batch_size => 100 ) { |articles| ... }
 # iterate over published articles in chunks of 100
Conditions
               - where -


• String conditions
• Array conditions
• Hash conditions
String Conditions


 Client.where("orders_count = ‘2’")
Array Conditions
Client.where("orders_count = ?", params[:orders])


Client.where("orders_count = ? AND locked = ?",
        params[:orders], false)


Client.where("orders_count = #{params[:orders]}")
Array Conditions
Client.where("orders_count = ?", params[:orders])


Client.where("orders_count = ? AND locked = ?",
        params[:orders], false)



                                           X
Client.where("orders_count = #{params[:orders]}")


                          hacking!!! by SQL injection
 http://guides.rubyonrails.org/security.html#sql-injection
Array Conditions
 - Placeholder conditions using symbols -

Client.where("created_at >= :start_date AND created_at
<= :end_date", {:start_date =>
params[:start_date], :end_date => params[:end_date]})
Array Conditions
                 - Range conditions -


  Client.where(:created_at => (params[:start_date].to_date)..
  (params[:end_date].to_date))




SELECT "clients".* FROM "clients" WHERE
("clients"."created_at" BETWEEN '2010-09-29' AND
'2010-11-30')
Hash Conditions
              - Equality conditions -


Client.where(:locked => true)
Hash Conditions
                 - Range conditions -


  Client.where(:created_at => (Time.now.midnight -
  1.day)..Time.now.midnight)




SELECT * FROM clients WHERE (clients.created_at BETWEEN
'2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
Hash Conditions
                  - Subset conditions -


  Client.where(:orders_count => [1,3,5])




SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
Ordering

Client.order("created_at")

Client.order("created_at DESC")
# OR
Client.order("created_at ASC")
Selecting
                                                                         ?
If the select method is used, all the returning objects will be read only.



     Client.select("viewable_by, locked")
       SELECT viewable_by, locked FROM clients
     ActiveModel::MissingAttributeError: missing attribute: <attribute>



     Client.select(:name).uniq
       SELECT DISTINCT name FROM clients


     query = Client.select(:name).uniq
     # => Returns unique names
     query.uniq(false)
     # => Returns all names, even if there are duplicates
Limit & Offset
Client.limit(5)

   SELECT * FROM clients LIMIT 5

Client.limit(5).offset(30)

   SELECT * FROM clients LIMIT 5 OFFSET 30
Group
  Order.select(
  "date(created_at) as ordered_date, sum(price) as total_price")
  .group("date(created_at)")




SQL
  SELECT date(created_at) as ordered_date, sum(price) as
  total_price FROM orders GROUP BY date(created_at)
Having
  Order.select(
  "date(created_at) as ordered_date, sum(price) as total_price")
  .group("date(created_at)")
  .having("sum(price) > ?", 100)




SQL
  SELECT date(created_at) as ordered_date, sum(price) as
  total_price FROM orders GROUP BY date(created_at) HAVING
  sum(price) > 100
Overriding
       Conditions
• except
• only
• reorder
• reverse_order
Overriding
                  Conditions
                                 - except -

Post.where('id > 10').limit(20).order('id asc').except(:order)



             SELECT * FROM posts WHERE id > 10 LIMIT 20
Overriding
                       Conditions
                                        - only -

Post.where('id > 10').limit(20).order('id desc').only(:order, :where)



             SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
Overriding
          Conditions
                      - reorder -
class Post < ActiveRecord::Base
  ..
  ..
  has_many :comments, :order => 'posted_at DESC'
end
 



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

SELECT * FROM posts WHERE id = 10 ORDER BY name
Overriding
                 Conditions
                          - reverse_order -

  Client.where("orders_count > 10").order(:name).reverse_order


SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC


  Client.where("orders_count > 10").reverse_order



SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
Readonly Objects
client = Client.readonly.first
client.visits += 1
client.save




     ActiveRecord::ReadOnlyRecord exception
Locking Records
    for Update
• To prevent “race conditions”
• To ensure “atomic updates”
• Two locking mechanisms
  ‣ Optimistic Locking : version control
  ‣ Pessimistic Locking : DB lock
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"
Optimistic Locking
• To turn off,
  ActiveRecord::Base.lock_optimistically = false

• To override the name of the lock_version
  column

  class Client < ActiveRecord::Base
    set_locking_column :lock_client_column
Pessimistic Locking

• A locking mechanism by DB
• An exclusive lock on the selected rows
• Usually wrapped inside a transaction
• Two types of Lock
 ‣ FOR UPDATE (default, an exclusive lock)
 ‣ LOCK IN SHARE MODE
Pessimistic Locking
  Item.transaction do
    i = Item.lock.first
    i.name = 'Jones'
    i.save




 SQL (0.2ms)   BEGIN
 Item Load (0.3ms)   SELECT * FROM `items` LIMIT 1 FOR UPDATE
 Item Update (0.4ms)   UPDATE `items` SET `updated_at` = '2009-02-07
 18:05:56', `name` = 'Jones' WHERE `id` = 1
 SQL (0.8ms)   COMMIT
Pessimistic Locking
 Item.transaction do
   i = Item.lock("LOCK IN SHARE MODE").find(1)
   i.increment!(:views)
 end




 item = Item.first
 item.with_lock do
   # This block is called within a transaction,
   # item is already locked.
   item.increment!(:views)
Joining Tables
        - Using a String SQL Fragment -


 Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id




SELECT clients.*
FROM clients
LEFT OUTER JOIN addresses
ON addresses.client_id = clients.id
Joining Tables
- Using Array/Hash of Named Associations -

              only with INNER JOIN


• a Single Association
• Multiple Associations
• Nested Associations(Single Level)
• Nested Associations(Multiple Level)
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • a Single Association
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Category.joins(:posts)
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest
end                                     SELECT categories.*
                                          FROM categories
class Guest < ActiveRecord::Base          INNER JOIN posts
  belongs_to :comment
end                                       ON posts.category_id = categories.id
 
class Tag < ActiveRecord::Base
  belongs_to :post                      “return a Category object for all categories with posts”
end
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                                                          and
  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
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
                                                                            nested
  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
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(Multiple Level)
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Category.joins(:posts => [{:comments
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest
end
                                        SELECT categories.* FROM categories
class Guest < ActiveRecord::Base          INNER JOIN posts ON posts.category_id = categories.id
  belongs_to :comment
                                          INNER JOIN comments ON comments.post_id = posts.id
end
                                          INNER JOIN guests ON guests.id = comments.quest_id
class Tag < ActiveRecord::Base            INNER JOIN tags ON tags.post_id = posts.id
  belongs_to :post
end
Joining Tables
  - Specifying Conditions on the Joined Tables -

: using Array and String Conditions

 time_range = (Time.now.midnight - 1.day)..Time.now.midnight
 Client.joins(:orders)




: using nested Hash Conditions

 time_range = (Time.now.midnight - 1.day)..Time.now.midnight
 Client.joins(:orders)
감사합니다.

More Related Content

PDF
ActiveRecord Query Interface (1), Season 1
KEY
Scala on Your Phone
PDF
The Ring programming language version 1.4.1 book - Part 13 of 31
PDF
The Ring programming language version 1.7 book - Part 48 of 196
PDF
Introduction to type classes in 30 min
PDF
The Ring programming language version 1.5.4 book - Part 44 of 185
PPTX
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
PDF
Introduction to type classes
ActiveRecord Query Interface (1), Season 1
Scala on Your Phone
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.7 book - Part 48 of 196
Introduction to type classes in 30 min
The Ring programming language version 1.5.4 book - Part 44 of 185
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Introduction to type classes

What's hot (16)

PDF
The Ring programming language version 1.5.3 book - Part 44 of 184
PDF
The Ring programming language version 1.10 book - Part 54 of 212
PDF
The Ring programming language version 1.6 book - Part 46 of 189
KEY
Node js mongodriver
PDF
Backbone.js: Run your Application Inside The Browser
PDF
PPTX
The uniform interface is 42
PPTX
CocoaHeads Moscow. Азиз Латыпов, VIPole. «Запросы в CoreData с агрегатными фу...
DOC
Adodb Scripts And Some Sample Scripts[1]
PPTX
13 networking, mobile services, and authentication
PDF
Hidden Treasures of the Python Standard Library
DOCX
VISUALIZAR REGISTROS EN UN JTABLE
PPTX
Unit testing powershell
PDF
dotSwift - From Problem to Solution
PDF
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
ODP
Intravert Server side processing for Cassandra
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.6 book - Part 46 of 189
Node js mongodriver
Backbone.js: Run your Application Inside The Browser
The uniform interface is 42
CocoaHeads Moscow. Азиз Латыпов, VIPole. «Запросы в CoreData с агрегатными фу...
Adodb Scripts And Some Sample Scripts[1]
13 networking, mobile services, and authentication
Hidden Treasures of the Python Standard Library
VISUALIZAR REGISTROS EN UN JTABLE
Unit testing powershell
dotSwift - From Problem to Solution
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
Intravert Server side processing for Cassandra
Ad

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

PDF
ActiveRecord Query Interface (2), Season 2
KEY
Active Record Query Interface (2), Season 1
PDF
From mysql to MongoDB(MongoDB2011北京交流会)
PPTX
Solving the n + 1 query problem
PPTX
Кирилл Безпалый, .NET Developer, Ciklum
PPTX
MongoDB World 2018: Keynote
PDF
Postgres & Redis Sitting in a Tree- Rimas Silkaitis, Heroku
PDF
Introduction to Active Record at MySQL Conference 2007
PPTX
Agile data presentation 3 - cambridge
PDF
Introduction to Active Record - Silicon Valley Ruby Conference 2007
PDF
Kickin' Ass with Cache-Fu (without notes)
 
PDF
Maior performance no seu sistema com o uso adequado de orm em rails
PDF
Litestack talk at Brighton 2024 (Unleashing the power of SQLite for Ruby apps)
KEY
PPTX
MongoDB (Advanced)
PDF
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
PDF
Mongodb workshop
PDF
Solr @ Etsy - Apache Lucene Eurocon
PDF
performance vamos dormir mais?
PDF
Introduction to-mongo db-execution-plan-optimizer-final
ActiveRecord Query Interface (2), Season 2
Active Record Query Interface (2), Season 1
From mysql to MongoDB(MongoDB2011北京交流会)
Solving the n + 1 query problem
Кирилл Безпалый, .NET Developer, Ciklum
MongoDB World 2018: Keynote
Postgres & Redis Sitting in a Tree- Rimas Silkaitis, Heroku
Introduction to Active Record at MySQL Conference 2007
Agile data presentation 3 - cambridge
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Kickin' Ass with Cache-Fu (without notes)
 
Maior performance no seu sistema com o uso adequado de orm em rails
Litestack talk at Brighton 2024 (Unleashing the power of SQLite for Ruby apps)
MongoDB (Advanced)
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
Mongodb workshop
Solr @ Etsy - Apache Lucene Eurocon
performance vamos dormir mais?
Introduction to-mongo db-execution-plan-optimizer-final
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
Week 4 Term 3 Study Techniques revisited.pptx
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PPTX
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
PDF
VCE English Exam - Section C Student Revision Booklet
PDF
Basic Mud Logging Guide for educational purpose
PDF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
PDF
STATICS OF THE RIGID BODIES Hibbelers.pdf
PPTX
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
PDF
Anesthesia in Laparoscopic Surgery in India
PDF
01-Introduction-to-Information-Management.pdf
PDF
Microbial disease of the cardiovascular and lymphatic systems
PDF
Complications of Minimal Access Surgery at WLH
PPTX
Renaissance Architecture: A Journey from Faith to Humanism
PDF
O5-L3 Freight Transport Ops (International) V1.pdf
PDF
2.FourierTransform-ShortQuestionswithAnswers.pdf
PDF
RMMM.pdf make it easy to upload and study
PDF
Origin of periodic table-Mendeleev’s Periodic-Modern Periodic table
PPTX
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
PPTX
Institutional Correction lecture only . . .
PPTX
Cell Structure & Organelles in detailed.
Week 4 Term 3 Study Techniques revisited.pptx
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
VCE English Exam - Section C Student Revision Booklet
Basic Mud Logging Guide for educational purpose
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
STATICS OF THE RIGID BODIES Hibbelers.pdf
school management -TNTEU- B.Ed., Semester II Unit 1.pptx
Anesthesia in Laparoscopic Surgery in India
01-Introduction-to-Information-Management.pdf
Microbial disease of the cardiovascular and lymphatic systems
Complications of Minimal Access Surgery at WLH
Renaissance Architecture: A Journey from Faith to Humanism
O5-L3 Freight Transport Ops (International) V1.pdf
2.FourierTransform-ShortQuestionswithAnswers.pdf
RMMM.pdf make it easy to upload and study
Origin of periodic table-Mendeleev’s Periodic-Modern Periodic table
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
Institutional Correction lecture only . . .
Cell Structure & Organelles in detailed.

Active Record Query Interface (1), Season 2

  • 1. Ror lab. season 2 - the 10th round - Active Record Query Interface (1) November 17th, 2012 Hyoseong Choi
  • 2. ActiveRecord • No more SQL statements MySQL PostgreSQL : select * from tables SQLite ... ORM Action Sequences Active 1. SQL query Record 2. Result set Finder 3. Ruby object Methods 4. (after_find callback)
  • 3. Finder Methods of ActiveRecord 1.where 2.select 3.group 4.order 5.reorder 6.reverse_order 7.limit 8.offset 9.joins 10.includes 11.lock 12.readonly 13.from 14.having ActiveRecord::Relation
  • 4. Retrieving A Single Object • find :ActiveRecord::RecordNotFound • first :nil • last :nil • first! : ActiveRecord::RecordNotFound • last! : ActiveRecord::RecordNotFound
  • 5. Retrieving A Single Object - find - # Find the client with primary key (id) 10. client = Client.find(10) SELECT * FROM clients WHERE (clients.id = 10) ActiveRecord::RecordNotFound exception
  • 6. Retrieving A Single Object - first - client = Client.first # => #<Client id: 1, first_name: "Lifo"> SELECT * FROM clients LIMIT 1 nil if no matching record is found
  • 7. Retrieving A Single Object - last - client = Client.last # => #<Client id: 221, first_name: "Russel"> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 nil if no matching record is found
  • 8. Retrieving A Single Object - first! - client = Client.first! # => #<Client id: 1, first_name: "Lifo"> SELECT * FROM clients LIMIT 1 RecordNotFound if no matching record
  • 9. Retrieving A Single Object - last! - client = Client.last! # => #<Client id: 221, first_name: "Russel"> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 RecordNotFound if no matching record
  • 10. Retrieving Multiple Objects • Using multiple primary keys • In batches • find_each :batch_size, :start, • find_in_batches :batch_size, :start + find options (except for :order, :limit)
  • 11. Retrieving Multiple Objects - Using multiple primary keys - # Find the clients with primary keys 1 and 10. client = Client.find([1, 10]) # Or even Client.find(1, 10) # => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, SELECT * FROM clients WHERE (clients.id IN (1,10)) ActiveRecord::RecordNotFound exception
  • 12. Retrieving Multiple Objects - in Batches - to iterate over a large set of records • find_each : each record to the block individually as a model • find_in_batches : the entire batch to the block as an array of models # This is very inefficient when the users table has thousands of rows. User.all.each do |user|   NewsLetter.weekly_deliver(user) OK to 1,000
  • 13. Retrieving Multiple Objects - in Batches : find_each - User.find_each do |user|   NewsLetter.weekly_deliver(user) end User.find_each(:batch_size => 5000) do |user|   NewsLetter.weekly_deliver(user) end User.find_each(:start => 2000, :batch_size => 5000) do | user|   NewsLetter.weekly_deliver(user)
  • 14. Retrieving Multiple Objects - in Batches : find_each - Article.find_each { |a| ... } # => iterate over all articles, in chunks of 1000 (the default) Article.find_each(:conditions => { :published => true }, :batch_size => 100 ) { |a| ... } # iterate over published articles in chunks of 100 http://archives.ryandaigle.com/articles/2009/2/23/what-s- new-in-edge-rails-batched-find
  • 15. Retrieving Multiple Objects - in Batches : find_in_batches - # Give add_invoices an array of 1000 invoices at a time Invoice.find_in_batches(:include => :invoice_lines) do | invoices|   export.add_invoices(invoices) end options : • :batch_size & :start • options of find method (except :order and :limit)
  • 16. Retrieving Multiple Objects - in Batches : find_in_batches - Article.find_in_batches { |articles| articles.each { |a| ... } } # => articles is array of size 1000 Article.find_in_batches(:batch_size => 100 ) { |articles| articles.each { | a| ... } } # iterate over all articles in chunks of 100 class Article < ActiveRecord::Base scope :published, :conditions => { :published => true } end Article.published.find_in_batches(:batch_size => 100 ) { |articles| ... } # iterate over published articles in chunks of 100
  • 17. Conditions - where - • String conditions • Array conditions • Hash conditions
  • 19. Array Conditions Client.where("orders_count = ?", params[:orders]) Client.where("orders_count = ? AND locked = ?", params[:orders], false) Client.where("orders_count = #{params[:orders]}")
  • 20. Array Conditions Client.where("orders_count = ?", params[:orders]) Client.where("orders_count = ? AND locked = ?", params[:orders], false) X Client.where("orders_count = #{params[:orders]}") hacking!!! by SQL injection http://guides.rubyonrails.org/security.html#sql-injection
  • 21. Array Conditions - Placeholder conditions using symbols - Client.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]})
  • 22. Array Conditions - Range conditions - Client.where(:created_at => (params[:start_date].to_date).. (params[:end_date].to_date)) SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30')
  • 23. Hash Conditions - Equality conditions - Client.where(:locked => true)
  • 24. Hash Conditions - Range conditions - Client.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight) SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
  • 25. Hash Conditions - Subset conditions - Client.where(:orders_count => [1,3,5]) SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
  • 27. Selecting ? If the select method is used, all the returning objects will be read only. Client.select("viewable_by, locked") SELECT viewable_by, locked FROM clients ActiveModel::MissingAttributeError: missing attribute: <attribute> Client.select(:name).uniq SELECT DISTINCT name FROM clients query = Client.select(:name).uniq # => Returns unique names query.uniq(false) # => Returns all names, even if there are duplicates
  • 28. Limit & Offset Client.limit(5) SELECT * FROM clients LIMIT 5 Client.limit(5).offset(30) SELECT * FROM clients LIMIT 5 OFFSET 30
  • 29. Group Order.select( "date(created_at) as ordered_date, sum(price) as total_price") .group("date(created_at)") SQL SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at)
  • 30. Having Order.select( "date(created_at) as ordered_date, sum(price) as total_price") .group("date(created_at)") .having("sum(price) > ?", 100) SQL SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100
  • 31. Overriding Conditions • except • only • reorder • reverse_order
  • 32. Overriding Conditions - except - Post.where('id > 10').limit(20).order('id asc').except(:order) SELECT * FROM posts WHERE id > 10 LIMIT 20
  • 33. Overriding Conditions - only - Post.where('id > 10').limit(20).order('id desc').only(:order, :where) SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
  • 34. Overriding Conditions - reorder - class Post < ActiveRecord::Base   ..   ..   has_many :comments, :order => 'posted_at DESC' end   SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC SELECT * FROM posts WHERE id = 10 ORDER BY name
  • 35. Overriding Conditions - reverse_order - Client.where("orders_count > 10").order(:name).reverse_order SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC Client.where("orders_count > 10").reverse_order SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
  • 36. Readonly Objects client = Client.readonly.first client.visits += 1 client.save ActiveRecord::ReadOnlyRecord exception
  • 37. Locking Records for Update • To prevent “race conditions” • To ensure “atomic updates” • Two locking mechanisms ‣ Optimistic Locking : version control ‣ Pessimistic Locking : DB lock
  • 38. 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"
  • 39. Optimistic Locking • To turn off, ActiveRecord::Base.lock_optimistically = false • To override the name of the lock_version column class Client < ActiveRecord::Base   set_locking_column :lock_client_column
  • 40. Pessimistic Locking • A locking mechanism by DB • An exclusive lock on the selected rows • Usually wrapped inside a transaction • Two types of Lock ‣ FOR UPDATE (default, an exclusive lock) ‣ LOCK IN SHARE MODE
  • 41. Pessimistic Locking Item.transaction do   i = Item.lock.first   i.name = 'Jones'   i.save SQL (0.2ms)   BEGIN Item Load (0.3ms)   SELECT * FROM `items` LIMIT 1 FOR UPDATE Item Update (0.4ms)   UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name` = 'Jones' WHERE `id` = 1 SQL (0.8ms)   COMMIT
  • 42. Pessimistic Locking Item.transaction do   i = Item.lock("LOCK IN SHARE MODE").find(1)   i.increment!(:views) end item = Item.first item.with_lock do   # This block is called within a transaction,   # item is already locked.   item.increment!(:views)
  • 43. Joining Tables - Using a String SQL Fragment - Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id
  • 44. Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN • a Single Association • Multiple Associations • Nested Associations(Single Level) • Nested Associations(Multiple Level)
  • 45. Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • a Single Association   belongs_to :category   has_many :comments   has_many :tags end Category.joins(:posts)   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest end SELECT categories.*   FROM categories class Guest < ActiveRecord::Base   INNER JOIN posts   belongs_to :comment end ON posts.category_id = categories.id   class Tag < ActiveRecord::Base   belongs_to :post “return a Category object for all categories with posts” end
  • 46. 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 and   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
  • 47. 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 nested   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
  • 48. 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(Multiple Level)   belongs_to :category   has_many :comments   has_many :tags end Category.joins(:posts => [{:comments   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest end   SELECT categories.* FROM categories class Guest < ActiveRecord::Base   INNER JOIN posts ON posts.category_id = categories.id   belongs_to :comment INNER JOIN comments ON comments.post_id = posts.id end   INNER JOIN guests ON guests.id = comments.quest_id class Tag < ActiveRecord::Base INNER JOIN tags ON tags.post_id = posts.id   belongs_to :post end
  • 49. Joining Tables - Specifying Conditions on the Joined Tables - : using Array and String Conditions time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders) : using nested Hash Conditions time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders)
  • 51.   ROR Lab.

Editor's Notes