Active Record Query Interface (1), Season 2
Upcoming SlideShare
Loading in...5
×
 

Active Record Query Interface (1), Season 2

on

  • 2,014 views

This

This

Statistics

Views

Total Views
2,014
Views on SlideShare
1,574
Embed Views
440

Actions

Likes
1
Downloads
14
Comments
0

5 Embeds 440

http://rorlab.org 231
http://lab.wordstory.net 187
http://www.rorlab.org 19
http://www.hanrss.com 2
https://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Active Record Query Interface (1), Season 2 Active Record Query Interface (1), Season 2 Presentation Transcript

  • Ror lab. season 2 - the 10th round - Active RecordQuery Interface (1) November 17th, 2012 Hyoseong Choi
  • ActiveRecord• No more SQL statements MySQL PostgreSQL : select * from tables SQLite ... ORMAction Sequences Active 1. SQL query Record 2. Result set Finder 3. Ruby object Methods 4. (after_find callback)
  • Finder Methods of ActiveRecord1.where2.select3.group4.order5.reorder6.reverse_order7.limit8.offset9.joins10.includes11.lock12.readonly13.from14.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 1nil 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 1nil if no matching record is found
  • Retrieving A Single Object - first! - client = Client.first! # => #<Client id: 1, first_name: "Lifo">SELECT * FROM clients LIMIT 1RecordNotFound 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 1RecordNotFound 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
  • RetrievingMultiple Objects - in Batches : find_each -User.find_each do |user|  NewsLetter.weekly_deliver(user)endUser.find_each(:batch_size => 5000) do |user|  NewsLetter.weekly_deliver(user)endUser.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 100http://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) endoptions : • :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| ... } } # => articlesis array of size 1000Article.find_in_batches(:batch_size => 100 ) { |articles| articles.each { |a| ... } } # iterate over all articles in chunks of 100class Article < ActiveRecord::Base scope :published, :conditions => { :published => true }endArticle.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 ConditionsClient.where("orders_count = ?", params[:orders])Client.where("orders_count = ? AND locked = ?", params[:orders], false)Client.where("orders_count = #{params[:orders]}")
  • Array ConditionsClient.where("orders_count = ?", params[:orders])Client.where("orders_count = ? AND locked = ?", params[:orders], false) XClient.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 AND2010-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 BETWEEN2008-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))
  • OrderingClient.order("created_at")Client.order("created_at DESC")# ORClient.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 & OffsetClient.limit(5) SELECT * FROM clients LIMIT 5Client.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 DESCend SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESCSELECT * FROM posts WHERE id = 10 ORDER BY name
  • Overriding Conditions - reverse_order - Client.where("orders_count > 10").order(:name).reverse_orderSELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC Client.where("orders_count > 10").reverse_orderSELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
  • Readonly Objectsclient = Client.readonly.firstclient.visits += 1client.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_idSELECT clients.*FROM clientsLEFT OUTER JOIN addressesON 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 JOINclass Category < ActiveRecord::Base  has_many :postsend class Post < ActiveRecord::Base • a Single Association  belongs_to :category  has_many :comments  has_many :tagsend Category.joins(:posts) class Comment < ActiveRecord::Base  belongs_to :post  has_one :guestend SELECT categories.*  FROM categoriesclass Guest < ActiveRecord::Base   INNER JOIN posts  belongs_to :commentend 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 JOINclass Category < ActiveRecord::Base  has_many :postsend class Post < ActiveRecord::Base • Multiple Associations  belongs_to :category  has_many :comments  has_many :tagsend Post.joins(:category, :comments) class Comment < ActiveRecord::Base  belongs_to :post and  has_one :guest SELECT posts.* FROM postsend    INNER JOIN categoriesclass Guest < ActiveRecord::Base ON posts.category_id = categories.id  belongs_to :comment   INNER JOIN commentsend ON comments.post_id = posts.id class Tag < ActiveRecord::Base “return all posts that have a category and at least one comment”  belongs_to :postend
  • Joining Tables - Using Array/Hash of Named Associations - only with INNER JOINclass Category < ActiveRecord::Base  has_many :postsend class Post < ActiveRecord::Base • Nested Associations(Single Level)  belongs_to :category  has_many :comments  has_many :tagsend Post.joins(:comments => :guest) class Comment < ActiveRecord::Base  belongs_to :post nested  has_one :guest SELECT posts.* FROM postsend    INNER JOIN commentsclass Guest < ActiveRecord::Base ON comments.post_id = posts.id  belongs_to :comment   INNER JOIN guestsend 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 JOINclass Category < ActiveRecord::Base  has_many :postsend class Post < ActiveRecord::Base • Nested Associations(Multiple Level)  belongs_to :category  has_many :comments  has_many :tagsend Category.joins(:posts => [{:comments class Comment < ActiveRecord::Base  belongs_to :post  has_one :guestend  SELECT categories.* FROM categoriesclass Guest < ActiveRecord::Base   INNER JOIN posts ON posts.category_id = categories.id  belongs_to :comment INNER JOIN comments ON comments.post_id = posts.idend  INNER JOIN guests ON guests.id = comments.quest_idclass Tag < ActiveRecord::Base INNER JOIN tags ON tags.post_id = posts.id  belongs_to :postend
  • 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)
  • 감사합니다.
  •    ROR Lab.