The 9th Round of ROR Lab.



Active Record
Query Interface(1)

      March 17th, 2012

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

      • Fire                     Finder
      • Ruby object             Methods
      • after_find callback
                                     ROR Lab.
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
                         ROR Lab.
Retrieving
    A Single Object
• find
• first
• last
• first!
• last!
                  ROR Lab.
Retrieving
   A Single Object
                        - find -


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

  client = Client.find(10)

  # => #<Client id: 10, first_name: "Ryan">



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

ActiveRecord::RecordNotFound exception


                                                ROR Lab.
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



                                             ROR Lab.
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



                                                 ROR Lab.
Retrieving
   A Single Object
                      - first! -


  client = Client.first!

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



SELECT * FROM clients LIMIT 1


RecordNotFound if no matching record



                                             ROR Lab.
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



                                                 ROR Lab.
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)


                                         ROR Lab.
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, first_name: "Ryan">]



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

ActiveRecord::RecordNotFound exception


                                                   ROR Lab.
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)
       end
                                              OK to 1,000
                                                             ROR Lab.
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)
end



                                                ROR Lab.
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

                                                     ROR Lab.
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)

                                                    ROR Lab.
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



                                                    ROR Lab.
Conditions
               - where -


• String conditions
• Array conditions
• Hash conditions

                           ROR Lab.
String Conditions


 Client.where("orders_count = ‘2’")




                                      ROR Lab.
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


                                                        ROR Lab.
Array Conditions
         - Placeholder conditions -

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




                                              ROR Lab.
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')




                                             ROR Lab.
Hash Conditions
           - Equality conditions -


Client.where(:locked => true)

Client.where('locked' => true)




                                     ROR Lab.
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')




                                                  ROR Lab.
Hash Conditions
               - Subset conditions -


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



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




                                                 ROR Lab.
Ordering

Client.order("created_at")

Client.order("created_at DESC")
# OR
Client.order("created_at ASC")

Client.order("orders_count ASC, created_at DESC")




                                                ROR Lab.
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


                                                                     ROR Lab.
Limit & Offset
Client.limit(5)

  SELECT * FROM clients LIMIT 5

Client.limit(5).offset(30)

  SELECT * FROM clients LIMIT 5 OFFSET 30




                                            ROR Lab.
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)




                                                     ROR Lab.
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




                                                     ROR Lab.
Overriding
       Conditions
• except
• only
• reorder
• reverse_order

                    ROR Lab.
Overriding
             Conditions
                         - except -

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



          SELECT * FROM posts WHERE id > 10 LIMIT 20




                                                       ROR Lab.
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




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



SELECT * FROM posts WHERE id = 10 ORDER BY name

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


                                                    ROR Lab.
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


                                                         ROR Lab.
Readonly Objects
client = Client.readonly.first
client.visits += 1
client.save




    ActiveRecord::ReadOnlyRecord exception




                                      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"
  c2.save # Raises an ActiveRecord::StaleObjectError



                                                  ROR Lab.
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
  end




                                             ROR Lab.
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

                                              ROR Lab.
Pessimistic Locking
  Item.transaction do
    i = Item.lock.first
    i.name = 'Jones'
    i.save
  end



 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




                                                           ROR Lab.
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)
 end




                                                  ROR Lab.
Joining Tables
       - Using a String SQL Fragment -


 Client.joins('LEFT OUTER JOIN addresses ON
 addresses.client_id = clients.id')



SELECT clients.*
FROM clients
LEFT OUTER JOIN addresses
ON addresses.client_id = clients.id




                                              ROR Lab.
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)




                                        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       • 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

                                                                                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.
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 =>
 
class Comment < ActiveRecord::Base
                                          [{:comments => :guest}, :tags]
  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

                                                                             ROR Lab.
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)
       .where('orders.created_at' => time_range)


: using nested Hash Conditions

 time_range = (Time.now.midnight - 1.day)..Time.now.midnight
 Client.joins(:orders)
       .where(:orders => {:created_at => time_range})


                                                     ROR Lab.
Joining Tables
    - Inner Join -




   SELECT <select_list>
   FROM TableA A
   INNER JOIN TableB B
   ON A.Key = B.Key



                          ROR Lab.
Joining Tables
              - Left Join -




SELECT <select_list>   SELECT <select_list>
FROM TableA A          FROM TableA A
LEFT JOIN TableB B     LEFT JOIN TableB B
ON A.Key = B.Key       ON A.Key = B.Key
                       WHERE B.Key IS NULL

                                        ROR Lab.
Joining Tables
             - Right Join -




SELECT <select_list>   SELECT <select_list>
FROM TableA A          FROM TableA A
RIGHT JOIN TableB B    RIGHT JOIN TableB B
ON A.Key = B.Key       ON A.Key = B.Key
                       WHERE A.Key IS NULL

                                        ROR Lab.
Joining Tables
           - Full Outer Join -




SELECT <select_list>     SELECT <select_list>
FROM TableA A            FROM TableA A
FULL OUTER JOIN TableB B FULL OUTER JOIN TableB B
ON A.Key = B.Key         ON A.Key = B.Key
                         WHERE A.Key IS NULL
                         OR B.Key IS NULL
                                            ROR Lab.
ROR Lab.
감사합니다.

ActiveRecord Query Interface (1), Season 1

  • 1.
    The 9th Roundof ROR Lab. Active Record Query Interface(1) March 17th, 2012 Hyoseong Choi ROR Lab.
  • 2.
    ActiveRecord • No moreSQL statements MySQL PostgreSQL : select * from tables SQLite ... ORM Active • SQL query Record • Fire Finder • Ruby object Methods • after_find callback ROR Lab.
  • 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 ROR Lab.
  • 4.
    Retrieving A Single Object • find • first • last • first! • last! ROR Lab.
  • 5.
    Retrieving A Single Object - find - # Find the client with primary key (id) 10. client = Client.find(10) # => #<Client id: 10, first_name: "Ryan"> SELECT * FROM clients WHERE (clients.id = 10) ActiveRecord::RecordNotFound exception ROR Lab.
  • 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 ROR Lab.
  • 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 ROR Lab.
  • 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 ROR Lab.
  • 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 ROR Lab.
  • 10.
    Retrieving MultipleObjects • Using multiple primary keys • In batches • find_each :batch_size, :start, • find_in_batches :batch_size, :start + find options (except for :order, :limit) ROR Lab.
  • 11.
    Retrieving MultipleObjects - 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, first_name: "Ryan">] SELECT * FROM clients WHERE (clients.id IN (1,10)) ActiveRecord::RecordNotFound exception ROR Lab.
  • 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) end OK to 1,000 ROR Lab.
  • 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) end ROR Lab.
  • 14.
    Retrieving MultipleObjects - 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 ROR Lab.
  • 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) ROR Lab.
  • 16.
    Retrieving MultipleObjects - 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 ROR Lab.
  • 17.
    Conditions - where - • String conditions • Array conditions • Hash conditions ROR Lab.
  • 18.
  • 19.
    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 ROR Lab.
  • 20.
    Array Conditions - Placeholder conditions - Client.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]}) ROR Lab.
  • 21.
    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') ROR Lab.
  • 22.
    Hash Conditions - Equality conditions - Client.where(:locked => true) Client.where('locked' => true) ROR Lab.
  • 23.
    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') ROR Lab.
  • 24.
    Hash Conditions - Subset conditions - Client.where(:orders_count => [1,3,5]) SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) ROR Lab.
  • 25.
    Ordering Client.order("created_at") Client.order("created_at DESC") # OR Client.order("created_atASC") Client.order("orders_count ASC, created_at DESC") ROR Lab.
  • 26.
    Selecting If the selectmethod 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 ROR Lab.
  • 27.
    Limit & Offset Client.limit(5) SELECT * FROM clients LIMIT 5 Client.limit(5).offset(30) SELECT * FROM clients LIMIT 5 OFFSET 30 ROR Lab.
  • 28.
    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) ROR Lab.
  • 29.
    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 ROR Lab.
  • 30.
    Overriding Conditions • except • only • reorder • reverse_order ROR Lab.
  • 31.
    Overriding Conditions - except - Post.where('id > 10').limit(20).order('id asc').except(:order) SELECT * FROM posts WHERE id > 10 LIMIT 20 ROR Lab.
  • 32.
    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 ROR Lab.
  • 33.
    Overriding Conditions - reorder - class Post < ActiveRecord::Base   ..   ..   has_many :comments, :order => 'posted_at DESC' end   Post.find(10).comments.reorder('name') SELECT * FROM posts WHERE id = 10 ORDER BY name SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC ROR Lab.
  • 34.
    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 ROR Lab.
  • 35.
    Readonly Objects client =Client.readonly.first client.visits += 1 client.save ActiveRecord::ReadOnlyRecord exception ROR Lab.
  • 36.
    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.
  • 37.
    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" c2.save # Raises an ActiveRecord::StaleObjectError ROR Lab.
  • 38.
    Optimistic Locking • Toturn 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 end ROR Lab.
  • 39.
    Pessimistic Locking • Alocking 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 ROR Lab.
  • 40.
    Pessimistic Locking Item.transaction do   i = Item.lock.first   i.name = 'Jones'   i.save end 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 ROR Lab.
  • 41.
    Pessimistic Locking Item.transactiondo   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) end ROR Lab.
  • 42.
    Joining Tables - Using a String SQL Fragment - Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id') SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id ROR Lab.
  • 43.
    Joining Tables - UsingArray/Hash of Named Associations - only with INNER JOIN • a Single Association • Multiple Associations • Nested Associations(Single Level) • Nested Associations(Multiple Level) ROR Lab.
  • 44.
    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 ROR Lab.
  • 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 • 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.
  • 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 • 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.
  • 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(Multiple Level)   belongs_to :category   has_many :comments   has_many :tags end Category.joins(:posts =>   class Comment < ActiveRecord::Base [{:comments => :guest}, :tags]   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 ROR Lab.
  • 48.
    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) .where('orders.created_at' => time_range) : using nested Hash Conditions time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders) .where(:orders => {:created_at => time_range}) ROR Lab.
  • 49.
    Joining Tables - Inner Join - SELECT <select_list> FROM TableA A INNER JOIN TableB B ON A.Key = B.Key ROR Lab.
  • 50.
    Joining Tables - Left Join - SELECT <select_list> SELECT <select_list> FROM TableA A FROM TableA A LEFT JOIN TableB B LEFT JOIN TableB B ON A.Key = B.Key ON A.Key = B.Key WHERE B.Key IS NULL ROR Lab.
  • 51.
    Joining Tables - Right Join - SELECT <select_list> SELECT <select_list> FROM TableA A FROM TableA A RIGHT JOIN TableB B RIGHT JOIN TableB B ON A.Key = B.Key ON A.Key = B.Key WHERE A.Key IS NULL ROR Lab.
  • 52.
    Joining Tables - Full Outer Join - SELECT <select_list> SELECT <select_list> FROM TableA A FROM TableA A FULL OUTER JOIN TableB B FULL OUTER JOIN TableB B ON A.Key = B.Key ON A.Key = B.Key WHERE A.Key IS NULL OR B.Key IS NULL ROR Lab.
  • 53.
  • 54.
  • 55.
      ROR Lab.