Your SlideShare is downloading. ×
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

ActiveRecord Query Interface (2), Season 2

1,140

Published on

Published in: Education
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,140
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
23
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Ror lab. season 2 - the 11th round - Active RecordQuery Interface (2) December 1st, 2012 Hyoseong Choi
  • 2. Eager Loading Associations : as few queries as possibleN + 1 queries problem clients = Client.limit(10)   clients.each do |client|   puts client.address.postcode end
  • 3. Eager Loading Associations : as few queries as possibleSolution to N + 1 queries problem Only 2 queries clients = Client.includes(:address).limit(10)   clients.each do |client|   puts client.address.postcode endSELECT * FROM clients LIMIT 10SELECT addresses.* FROM addresses  WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
  • 4. Eager Loading Associations- Eager Loading Multiple Associations - Array of Multiple Associations Post.includes(:category, :comments) Nested Associations Hash Category.includes( :posts => [{:comments => :guest}, :tags]).find(1)
  • 5. Eager Loading Associations- Conditions on Eager Loaded Associations - conditional “joins” > conditional “includes” Post.includes(:comments) .where("comments.visible", true) SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1)
  • 6. Eager Loading Associations- Conditions on Eager Loaded Associations - INNER JOIN LEFT OUTER JOIN conditional “joins” > conditional “includes” Post.includes(:comments) .where("comments.visible", true) SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1)
  • 7. Scopes• ToSQL AST(Abstract Syntax Tree) manger for Ruby :a specify ARel queries• Referenced as method calls on the association objects or model• finder methods in a scope• return an ActiveRecord::Relation object
  • 8. Scopesclass Post < ActiveRecord::Base  scope :published, where(:published => true)end ARel query
  • 9. Scopeschainableclass Post < ActiveRecord::Base  scope :published, where(:published => true).joins(:category)endchainable within scopesclass Post < ActiveRecord::Base  scope :published, where(:published => true)  scope :published_and_commented,published.and(self.arel_table[:comments_count].gt(0))end
  • 10. Scopes To call the scopePost.published # => [published posts]category = Category.firstcategory.posts.published# => [published posts belonging to this category]
  • 11. Scopes Working with timesclass Post < ActiveRecord::Base  scope :last_week, lambda { where("created_at < ?", Time.zone.now ) }end a lambda so that the scope is evaluated every time
  • 12. Scopes Passing in argumentsclass Post < ActiveRecord::Base  scope :1_week_before, lambda { |time| where("created_at < ?", time) }endPost.1_week_before(Time.zone.now)class Post < ActiveRecord::Base  def self.1_week_before(time)    where("created_at < ?", time)  endend ***What about “as a class method” ?
  • 13. Scopes Passing in arguments class Post < ActiveRecord::Base   scope :1_week_before, lambda { |time| where("created_at < ?", time) } end Post.1_week_before(Time.zone.now) b le r ea e fe class Post < ActiveRecord::Basepr   def self.1_week_before(time)     where("created_at < ?", time)   end end ***What about “as a class method” ?
  • 14. Scopes Passing in argumentscategory.posts.1_week_before(time)
  • 15. Scopes Working with Scopesclient = Client.find_by_first_name("Ryan")orders = client.orders.scopedorders.where("created_at > ?", 30.days.ago)
  • 16. Scopes Applying a default scopeclass Client < ActiveRecord::Base  default_scope where("removed_at IS NULL")endSELECT * FROM clients WHERE removed_at IS NULL
  • 17. Scopes Removing all scopesClient.unscoped.all Especially useful, when escaping the default_scope
  • 18. Dynamic Finders• Model.find_by_attribute_name• Model.find_all_by_attribute_name• Model.find_last_by_attribute_name• Model.find_by_attribute_name!• Model.find_by_attr1_and_attr2Rails 3.2 => Argument Error on missing fields
  • 19. Find or build a new object• first_or_create• first_or_create! Exception on invalid• first_or_initialize
  • 20. Find or build a new object• first_or_create Client.where(:first_name => Andy).first_or_create(:locked => false) # => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27"> Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false)SELECT * FROM clients WHERE (clients.first_name = Andy) LIMIT 1BEGININSERT INTO clients (created_at, first_name, locked, orders_count, updated_at)VALUES (2011-08-30 05:22:57, Andy, 0, NULL, 2011-08-30 05:22:57)COMMIT
  • 21. Find or build a new object• first_or_create! class Client < ActiveRecord::Base validates :orders_count, :presence => true end Client.where(:first_name => Andy).first_or_create! (:locked => false) # => ActiveRecord::RecordInvalid: Validation failed: Orders count cant be blank
  • 22. Find or build a new object• first_or_initialize nick = Client.where(:first_name => Nick).first_or_initialize(:locked => false) # => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">   SELECT * FROM clients WHERE (clients.first_name = Nick) LIMIT 1 nick.persisted? # => false nick.new_record? # => true nick.save # => truepersisted? vs new_record?
  • 23. Finding by SQL : return an array of objects where each object indicates a recordClient.find_by_sql("SELECT * FROM clients  INNER JOIN orders ON clients.id = orders.client_id  ORDER clients.created_at desc")cf. connection.select_all an array of hash
  • 24. select_all : return an array of hash where each hash indicates a recordClient.connection.select_all("SELECT * FROM clientsWHERE id = 1")cf. find_by_sql => instantiates objects
  • 25. pluckTo query a single column from the underlying table of a modelTo return an array of valuesClient.where(:active => true).pluck(:id)# SELECT id FROM clients WHERE active = 1 Client.uniq.pluck(:role)# SELECT DISTINCT role FROM clientsClient.select(:id).map { |c| c.id } Client.pluck(:id)
  • 26. Existence of ObjectsClient.exists?(1)Client.exists?(1,2,3)# orClient.exists?([1,2,3])Client.where(:first_name => Ryan).exists?Client.exists?cf. find method
  • 27. Existence of Objects# via a modelPost.any?Post.many? # via a named scopePost.recent.any?Post.recent.many? # via a relationPost.where(:published => true).any?Post.where(:published => true).many? # via an associationPost.first.categories.any?Post.first.categories.many?
  • 28. Calculations Client.count # SELECT count(*) AS count_all FROM clients Client.where(:first_name => Ryan).count # SELECT count(*) AS count_all FROM clients WHERE (first_name = Ryan) Client.includes("orders").where(:first_name => Ryan, :orders => {:status => received}).countSELECT 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)
  • 29. CalculationsClient.count(:age)Client.average("orders_count")Client.minimum("age")Client.maximum("age")Client.sum("orders_count")
  • 30. Running EXPLAIN a pretty printing that emulates the one of the database shells User.where(:id => 1).joins(:posts).explainEXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id`WHERE `users`.`id` = 1+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra       |+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |             ||  1 | SIMPLE      | posts | ALL   | NULL          | NULL    | NULL    | NULL  |    1 | Using where |+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+2 rows in set (0.00 sec)under MySQL
  • 31. Running EXPLAIN a pretty printing that emulates the one of the database shells User.where(:id => 1).joins(:posts).explainEXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"WHERE "users"."id" = 1                                  QUERY PLAN------------------------------------------------------------------------------ Nested Loop Left Join  (cost=0.00..37.24 rows=8 width=0)   Join Filter: (posts.user_id = users.id)   ->  Index Scan using users_pkey on users  (cost=0.00..8.27 rows=1 width=4)         Index Cond: (id = 1)   ->  Seq Scan on posts  (cost=0.00..28.88 rows=8 width=4)         Filter: (posts.user_id = 1)(6 rows)under PostgreSQL
  • 32. Running EXPLAIN Eager Loading User.where(:id => 1).includes(:posts).explain EXPLAIN for: SELECT `users`.* FROM `users`  WHERE `users`.`id` = 1 +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ |  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 1 row in set (0.00 sec)   EXPLAIN for: SELECT `posts`.* FROM `posts`  WHERE `posts`.`user_id` IN (1) +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ |  1 | SIMPLE      | posts | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)under MySQL
  • 33. Running EXPLAIN Automatic EXPLAINTo set the threshold in secondsconfig.active_record.auto_explain_threshold_in_seconds• disables automatic EXPLAIN => nil or no AR logger• Development mode => default, 0.5 sec• Test mode => nil• Production mode => nil
  • 34. Running EXPLAIN Disabling Automatic EXPLAINTo be selectively silenced with ActiveRecord::Base.silence_auto_explain do   # no automatic EXPLAIN is triggered here end• useful for queries are slow but fine : a heavyweight report of a admin interface
  • 35. 감사합니다.
  • 36.  

×