Your SlideShare is downloading. ×
0
Taking ActiveRecord to the Next Level <ul><li>Blythe Dunham </li></ul><ul><li>[email_address] </li></ul><ul><li>http://sno...
Goal <ul><li>Leverage advanced MySQL functionality  </li></ul><ul><li>with ActiveRecord </li></ul>
Disclaimer!!!! Premature Optimization
ActiveRecord 101 <ul><li>What's going on under the covers? </li></ul>
ActiveRecord 101 Database  Table  ff Active Record Model CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `nam...
ActiveRecord 101 with Animals! Database  Table  ff Active Record Model CREATE TABLE `animals` ( `id` int(11) NOT NULL auto...
Creating a Record <ul><li>animal = Animal.new </li></ul><ul><li>animal.name = 'Jerry Giraffe' </li></ul><ul><li>animal.pas...
Updating a Record <ul><li>animal.name = 'Jerry G' </li></ul><ul><li>animal.save! </li></ul><ul><li>UPDATE `animals`  </li>...
Finding a Record <ul><li>jerry = Animal.find :first,  </li></ul><ul><li>:conditions => ['name = ?', 'Jerry G'] </li></ul><...
Representing Relationships Species name Animal name email password fav_beer updated_at created_at species_id
Representing Relationships(DDL) CREATE TABLE `animals` ( `id` int(11) NOT NULL auto_increment, `name` varchar(35) NOT NULL...
Representing Relationships (AR) class Animal < ActiveRecord::Base belongs_to :species end class Species < ActiveRecord::Ba...
Representing Relationships (AR) jerry.species SELECT * FROM `species`  WHERE (`species`.`id` = 1)   species.animals SELECT...
Representing Relationships (AR) giraffe = Species.find_by_name 'giraffe' giraffe.animals << jerry SELECT * FROM `species` ...
Migration <ul><li>Set limits </li></ul><ul><li>Set default values </li></ul><ul><li>Identify NOT NULL columns </li></ul><u...
Migration 101 ruby script/generate scaffold Animal name:string password:string email:string fav_beer:string class CreateAn...
Set Limits Default String is VARCHAR(255) create_table :animals do |t| t.string :name,  :limit => 35 t.string :password,  ...
Numeric Type Limits t.integer :mysmallint,  :limit => 2 &quot;Smart types&quot; determines numeric type for MySQL 8 bytes ...
Set columns to NOT NULL create_table :animals do |t| t.string :name,  :limit => 35,   :null => false t.string :password, :...
Set default values create_table :animals do |t| t.string :name,  :limit => 35,  :null => false t.string :password, :limit ...
Remove unneeded columns create_table :animals do |t| t.string :name,  :limit => 35,    :null => false t.string :password, ...
Enumerated Column Plugin create_table :animals do |t| t.string :name,  :limit => 35,  :null => false t.string :password, :...
Think about the table parameters create_table :animals, :options => 'ENGINE=MyISAM'  do |t| t.string :name,  :limit => 35,...
Custom DDL create_table :animals do |t| t.string :name,  :limit => 35,  :null => false t.string :email,  :limit => 40,  :d...
Create (Unique) Indices create_table :species do |t| t.string :name, :null => false, :limit => 25  end add_index :species,...
ActiveRecord Uniqueness <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>validates_uniqueness_of :name </li></...
I Heart Foreign Keys <ul><li>Referential Integrity </li></ul>
The AR Way: Foreign Keys <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>has_many :animals, :dependent =>  :n...
The Rails Way: Foreign Keys <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>has_many :animals, :dependent => ...
Redhills Foreign Key Migration Plugin to the rescue! <ul><li>add_column :animals, :species_id, :integer, </li></ul><ul><li...
Primary Keys CREATE TABLE `animals` ( `id` int(11) NOT NULL auto_increment, `name` varchar(35) NOT NULL, `email` varchar(4...
Modify the Rails Primary Key Change type with change_column  MySQL Migration Optimization Plugin   create_table :animals, ...
Advanced ActiveRecord <ul><li>Insert and update options </li></ul><ul><li>Import bulk data </li></ul><ul><li>Finder Option...
ActiveRecord on Steroids: ar-extensions plugin <ul><li>Additional Create and Update options </li></ul><ul><li>save(options...
:ignore <ul><li>Standard ActiveRecord: </li></ul><ul><li>Create the record if it does not already exist </li></ul><ul><li>...
:ignore <ul><li>Ignore duplicates! One query, less code, fewer queries! </li></ul><ul><li>Animal.create!({:name => 'Jerry ...
:on_duplicate_key_update <ul><li>Update the record if it exists, if not create a new one. </li></ul><ul><li>A lot of code ...
:on_duplicate_key_update <ul><li>jerry = Animal.new :name => 'Jerry G',  </li></ul><ul><li>:password => 'frenchfry' </li><...
Reloading the instance <ul><li>AR Data can become inconsistent with DB after an  IGNORE, UPDATE, or ON DUPLICATE KEY UPDAT...
More Customization <ul><li>jerry.save(:keywords => 'LOW_PRIORITY',  </li></ul><ul><li>:pre_sql => '/*Pre comment*/', </li>...
Import (Bulk Insert) <ul><li>Instead of one-by-one, insert a ton of records fast </li></ul>
Import (Bulk Insert) <ul><li>Standard way: Insert each animal one by one </li></ul><ul><li>Animal.create!(:name => 'dolly ...
Fast Import: One INSERT  <ul><li>animals = [ Animal.new(:name => 'dolly dolphin',  </li></ul><ul><li>:password => 'dolly')...
Fastest Import: fewer columns <ul><li>columns = [ :name, :password ] </li></ul><ul><li>values = [['dolly dolphin', 'dolly'...
Insert Select <ul><li>Standard: Query and Insert one by one </li></ul><ul><li>Species.find(:all).each do |s| </li></ul><ul...
Insert Select Import <ul><li>SpeciesZoo.insert_select( </li></ul><ul><ul><li>:from => :species, </li></ul></ul><ul><ul><li...
Temporary Tables <ul><li>Not so good for slave replication </li></ul><ul><li>Can be used as a sandbox then imported into a...
Customizing Find <ul><li>Additional finder options </li></ul><ul><li>:keywords  </li></ul><ul><li>:pre_sql </li></ul><ul><...
Customizing Find <ul><li>Animal.find(:all, </li></ul><ul><li>:conditions => ['name = ?', 'Jerry G'], </li></ul><ul><li>:ke...
Need more? Get dirty with find_by_sql <ul><li>sql = Animal.send :finder_sql_to_string, </li></ul><ul><li>  :conditions => ...
More: find_union & count_union <ul><li>Animal.find_union( </li></ul><ul><li>{:conditions => ['animals.name like ?', 'Jerry...
Finder Issues: Speed and Memory <ul><li>paginate - less loaded into memory  </li></ul><ul><li>:select  option - Data is re...
Paginated Finders <ul><li>Rails 2.3.2 includes  :batch_size  option </li></ul><ul><li>Animal.find_each(:batch_size => 2) d...
:select <ul><li>Data is retrieved faster when fewer columns are selected </li></ul><ul><li>Animal.find :first, :select => ...
:include hates :select <ul><li>But :select is ignored with eager loading (:include)! </li></ul><ul><li>Animal.find :first,...
Alternatives to Eager Loading  <ul><li>Eager loading for sparse :include can be time consuming </li></ul><ul><li>Use :join...
:join instead of :include  <ul><li>Eager loading is slow in Rails and can be slow on the </li></ul><ul><li>database. </li>...
Force it with Eager loading plugins <ul><li>Eager loading is slow in Rails and can be slow on the </li></ul><ul><li>databa...
Help from Rails 2  Query Cache  <ul><li>Animals of the same species are only loaded once </li></ul><ul><li>ActiveRecord::B...
ActiveRecordContext Plugin  <ul><li>ActiveRecord::Base.with_context do </li></ul><ul><li>animals = Animal.find :all </li><...
Piggyback Plugin  <ul><li>Delegate records with :has_one and :belongs_to  associations  </li></ul><ul><li>Great for access...
Piggyback Plugin  <ul><li>Uses joins to delegate records from :has_one and :belongs_to  </li></ul><ul><li>associations  </...
Avoiding Deadlock <ul><li>Deadlock Retry  plugin - retries query up to 3 times </li></ul><ul><li>Batch Operations (AR-Exte...
Reading from a Replica <ul><li>Masochism plugin  </li></ul>
Export Data <ul><li>ar-fixtures: Export entire table to yaml </li></ul><ul><li>ar-extensions:  export to csv </li></ul><ul...
Cache! Show me the money! <ul><li>Query Cache  </li></ul><ul><li>Memcache </li></ul><ul><li>Static Record Cache plugin </l...
ActiveRecord plugins and gems <ul><li>AR-Extensions  http://www. continuousthinking .com/tags/ arext </li></ul><ul><li>Pig...
Other Useful Plugins <ul><li>Migration Plugins </li></ul><ul><li>Redhills Consulting Foreign Key Migrations  http:// agile...
Questions? <ul><li>Thanks for attending! I hope you are on the next level. </li></ul><ul><li>Slides available at:  http://...
Pics <ul><li>Biz card:  http://www.twozdai.com/ </li></ul><ul><li>Toolbox:  http://www.flickr.com/photos/29802022@N07/2971...
Upcoming SlideShare
Loading in...5
×

MySQLConf2009: Taking ActiveRecord to the Next Level

2,010

Published on

Taking ActiveRecord to the next level contains tips and tricks for using ActiveRecord with enterprise Ruby on Rails Applications. Learn how to import and export multiple records, read off replicas, handle deadlocks, and use temporary tables. Use MySQL functionality such as adding index hints, on duplicate key update, insert select and more.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,010
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
42
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • Transcript of "MySQLConf2009: Taking ActiveRecord to the Next Level"

    1. 1. Taking ActiveRecord to the Next Level <ul><li>Blythe Dunham </li></ul><ul><li>[email_address] </li></ul><ul><li>http://snowgiraffe.com </li></ul>
    2. 2. Goal <ul><li>Leverage advanced MySQL functionality </li></ul><ul><li>with ActiveRecord </li></ul>
    3. 3. Disclaimer!!!! Premature Optimization
    4. 4. ActiveRecord 101 <ul><li>What's going on under the covers? </li></ul>
    5. 5. ActiveRecord 101 Database Table ff Active Record Model CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) default NULL, `password` varchar(255) default NULL, `email` varchar(255) default NULL, `created_at` datetime default NULL, `updated_at` datetime default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 class User < ActiveRecord::Base end
    6. 6. ActiveRecord 101 with Animals! Database Table ff Active Record Model CREATE TABLE `animals` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) default NULL, `password` varchar(255) default NULL, `email` varchar(255) default NULL, `species_id` int(11) default NULL, `created_at` datetime default NULL, `updated_at` datetime default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 class Animal < ActiveRecord::Base end
    7. 7. Creating a Record <ul><li>animal = Animal.new </li></ul><ul><li>animal.name = 'Jerry Giraffe' </li></ul><ul><li>animal.password = 'jerry' </li></ul><ul><li>animal.save! </li></ul><ul><li>INSERT INTO `animals` </li></ul><ul><li>(`name`, `updated_at`, `species_id`, </li></ul><ul><li>`password`, `email`, `created_at`) </li></ul><ul><li>VALUES('Jerry Giraffe', '2009-03-15 00:48:28', </li></ul><ul><li>NULL, 'jerry', NULL, '2009-03-15 00:48:28') </li></ul>
    8. 8. Updating a Record <ul><li>animal.name = 'Jerry G' </li></ul><ul><li>animal.save! </li></ul><ul><li>UPDATE `animals` </li></ul><ul><li>SET `updated_at` = '2009-03-15 03:01:06', </li></ul><ul><li>`name` = 'Jerry G' </li></ul><ul><li>WHERE `id` = 1 </li></ul>
    9. 9. Finding a Record <ul><li>jerry = Animal.find :first, </li></ul><ul><li>:conditions => ['name = ?', 'Jerry G'] </li></ul><ul><li>SELECT * FROM `animals` </li></ul><ul><li>WHERE (name = 'Jerry G') LIMIT 1 </li></ul>#shortcut Animal.find_by_name 'Jerry G'
    10. 10. Representing Relationships Species name Animal name email password fav_beer updated_at created_at species_id
    11. 11. Representing Relationships(DDL) CREATE TABLE `animals` ( `id` int(11) NOT NULL auto_increment, `name` varchar(35) NOT NULL, `email` varchar(40) default NULL, `fav_beer` enum('Guiness','Pabst','Redhook','Chimay') default 'Pabst', `created_at` datetime default NULL, `updated_at` datetime default NULL, `password` varchar(25) character set latin1 collate latin1_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `species` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255), PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
    12. 12. Representing Relationships (AR) class Animal < ActiveRecord::Base belongs_to :species end class Species < ActiveRecord::Base has_many :animals end
    13. 13. Representing Relationships (AR) jerry.species SELECT * FROM `species` WHERE (`species`.`id` = 1) species.animals SELECT * FROM `animals` WHERE (`animals`.species_id = 1)
    14. 14. Representing Relationships (AR) giraffe = Species.find_by_name 'giraffe' giraffe.animals << jerry SELECT * FROM `species` WHERE (`species`.`name` = 'giraffe' ) LIMIT 1 UPDATE `animals` SET `species_id` = 1, `updated_at` = '2009-03-19 23:15:54' WHERE `id` = 7
    15. 15. Migration <ul><li>Set limits </li></ul><ul><li>Set default values </li></ul><ul><li>Identify NOT NULL columns </li></ul><ul><li>Use enumerated columns </li></ul><ul><li>Custom DDL </li></ul><ul><li>Add (unique) indices </li></ul><ul><li>Foreign Keys are great </li></ul><ul><li>Primary Key Modifications </li></ul>
    16. 16. Migration 101 ruby script/generate scaffold Animal name:string password:string email:string fav_beer:string class CreateAnimals < ActiveRecord::Migration def self.up create_table :animals do |t| t.string :name t.string :password t.string :email t.string :fav_beer t.timestamps end end def self.down drop_table :animals end end
    17. 17. Set Limits Default String is VARCHAR(255) create_table :animals do |t| t.string :name, :limit => 35 t.string :password, :limit => 25 t.string :email, :limit => 40 t.string :fav_beer, :limit => 40 t.timestamps end
    18. 18. Numeric Type Limits t.integer :mysmallint, :limit => 2 &quot;Smart types&quot; determines numeric type for MySQL 8 bytes bigint 5 to 8 4 bytes int(11) 4, nil, 11 3 bytes mediumint 3 2 bytes smallint 2 1 byte tinyint 1 Column Size Numeric Type :limit
    19. 19. Set columns to NOT NULL create_table :animals do |t| t.string :name, :limit => 35, :null => false t.string :password, :limit => 25, :null => false t.string :email, :limit => 40 t.string :fav_beer, :limit => 40 t.timestamps end
    20. 20. Set default values create_table :animals do |t| t.string :name, :limit => 35, :null => false t.string :password, :limit => 25, :null => false t.string :email, :limit => 40, :default => nil t.string :fav_beer, :limit => 40 :default => 'Pabst' t.timestamps end
    21. 21. Remove unneeded columns create_table :animals do |t| t.string :name, :limit => 35, :null => false t.string :password, :limit => 25, :null => false t.string :email, :limit => 40, :default => nil t.string :fav_beer, :limit => 40 :default => 'Pabst' t.timestamps end
    22. 22. Enumerated Column Plugin create_table :animals do |t| t.string :name, :limit => 35, :null => false t.string :password, :limit => 25, :null => false t.string :email, :limit => 40, :default => nil t.enum :fav_beer, :default => 'Pabst' :limit => %w(Chimay Pabst Redhook) t.timestamps end
    23. 23. Think about the table parameters create_table :animals, :options => 'ENGINE=MyISAM' do |t| t.string :name, :limit => 35, :null => false t.string :password, :limit => 25, :null => false t.string :email, :limit => 40, :default => nil t.enum :fav_beer, :default => nil :limit => %w(Chimay Pabst Redhook) t.timestamps end
    24. 24. Custom DDL create_table :animals do |t| t.string :name, :limit => 35, :null => false t.string :email, :limit => 40, :default => nil t.enum :fav_beer, :default => nil :limit => %w(Chimay Pabst Redhook) t.timestamps end #case sensitive password (encrypted) execute &quot;ALTER TABLE `animals` ADD `password` varchar(25) character set latin1 collate latin1_bin NOT NULL&quot;
    25. 25. Create (Unique) Indices create_table :species do |t| t.string :name, :null => false, :limit => 25 end add_index :species, :name, :unique => true, :name => 'uk_species_name'
    26. 26. ActiveRecord Uniqueness <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>validates_uniqueness_of :name </li></ul><ul><li>end </li></ul><ul><li>Doesn't Guaranty Data Integrity! </li></ul>
    27. 27. I Heart Foreign Keys <ul><li>Referential Integrity </li></ul>
    28. 28. The AR Way: Foreign Keys <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>has_many :animals, :dependent => :nullify </li></ul><ul><li>end </li></ul>
    29. 29. The Rails Way: Foreign Keys <ul><li>class Species < ActiveRecord::Base </li></ul><ul><li>has_many :animals, :dependent => :nullify </li></ul><ul><li>end </li></ul><ul><li>Dependent Value SQL Equivalent: </li></ul><ul><li>:nullify => ON DELETE SET NULL </li></ul><ul><li>:delete_all => ON DELETE CASCADE </li></ul><ul><li>:destroy => No SQL equivalent. Every association is instantiated and and callbacks are executed before destruction </li></ul>
    30. 30. Redhills Foreign Key Migration Plugin to the rescue! <ul><li>add_column :animals, :species_id, :integer, </li></ul><ul><li>:references => :species, </li></ul><ul><li>:name => 'fk_animal_species', </li></ul><ul><li>:on_delete => :set_null, </li></ul><ul><li>:on_update => :cascade </li></ul><ul><li>ALTER TABLE `animals` ADD `species_id` int(11); </li></ul><ul><li>ALTER TABLE animals ADD CONSTRAINT fk_animal_species FOREIGN KEY (species_id) REFERENCES species (id) ON UPDATE CASCADE ON DELETE SET NULL; </li></ul>
    31. 31. Primary Keys CREATE TABLE `animals` ( `id` int(11) NOT NULL auto_increment, `name` varchar(35) NOT NULL, `email` varchar(40) default NULL, `fav_beer` enum('Guiness','Pabst','Redhook','Chimay') default 'Pabst', `species_id` int(11) default NULL, `created_at` datetime default NULL, `updated_at` datetime default NULL, `password` varchar(25) character set latin1 collate latin1_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `species` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255), PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
    32. 32. Modify the Rails Primary Key Change type with change_column MySQL Migration Optimization Plugin create_table :animals, :primary_key => &quot;special_key&quot;, :primary_column => { :type => :integer, :limit => 2, :precision => :unsigned, :scale => 3 } do |t| end CREATE TABLE `animals` (`special_key` smallint(3) UNSIGNED NOT NULL auto_increment PRIMARY KEY) ENGINE=InnoDB;
    33. 33. Advanced ActiveRecord <ul><li>Insert and update options </li></ul><ul><li>Import bulk data </li></ul><ul><li>Finder Options </li></ul><ul><li>Tools: plugin AR-Extensions </li></ul>
    34. 34. ActiveRecord on Steroids: ar-extensions plugin <ul><li>Additional Create and Update options </li></ul><ul><li>save(options={}) </li></ul><ul><li>save!(options={}) </li></ul><ul><li>create(args, options={}) </li></ul><ul><li>create!(args, options={}) </li></ul><ul><li>Options </li></ul><ul><li>:ignore </li></ul><ul><li>:on_duplicate_key_update </li></ul><ul><li>:keywords </li></ul><ul><li>:reload </li></ul><ul><li>:pre_sql </li></ul><ul><li>:post_sql </li></ul>
    35. 35. :ignore <ul><li>Standard ActiveRecord: </li></ul><ul><li>Create the record if it does not already exist </li></ul><ul><li>unless Animal.find_by_name('Jerry G') </li></ul><ul><li>Animal.create!(:name => 'Jerry G', </li></ul><ul><li>:password => 'jerry') </li></ul><ul><li>end </li></ul>
    36. 36. :ignore <ul><li>Ignore duplicates! One query, less code, fewer queries! </li></ul><ul><li>Animal.create!({:name => 'Jerry G', </li></ul><ul><li>:password => 'jerry'}, </li></ul><ul><li> {:ignore => true}) </li></ul>
    37. 37. :on_duplicate_key_update <ul><li>Update the record if it exists, if not create a new one. </li></ul><ul><li>A lot of code to upsert and performs two SQL queries! </li></ul><ul><li>jerry = Animal.find_by_name 'Jerry G' </li></ul><ul><li>jerry ||= Animal.new(:name => 'Jerry G') </li></ul><ul><li>jerry.password = 'frenchfry' </li></ul><ul><li>jerry.save! </li></ul>
    38. 38. :on_duplicate_key_update <ul><li>jerry = Animal.new :name => 'Jerry G', </li></ul><ul><li>:password => 'frenchfry' </li></ul><ul><li>jerry.save! :on_duplicate_key_update => </li></ul><ul><li>[:password, :updated_at] </li></ul><ul><li>INSERT INTO animals </li></ul><ul><li>(`name`, `updated_at`, `species_id`, </li></ul><ul><li>`password`,`email`, `created_at`) </li></ul><ul><li>VALUES('Jerry G', '2009-03-15 06:17:51', NULL, </li></ul><ul><li>'frenchfry', NULL, '2009-03-15 06:17:51') </li></ul><ul><li>ON DUPLICATE KEY UPDATE </li></ul><ul><li>`animals`.`password`=VALUES(`password`), </li></ul><ul><li>`animals`.`updated_at`=VALUES(`updated_at`) </li></ul>
    39. 39. Reloading the instance <ul><li>AR Data can become inconsistent with DB after an IGNORE, UPDATE, or ON DUPLICATE KEY UPDATE </li></ul><ul><li>reload executes more queries </li></ul><ul><li>For UPDATE the duplicate is automatically reloaded </li></ul><ul><li>jerry.email = 'jerry@snowgiraffe.com' </li></ul><ul><li>jerry.save! :on_duplicate_key_update => </li></ul><ul><li>[:password, :updated_at], </li></ul><ul><li>:reload => true, </li></ul><ul><li>:duplicate_columns => [:name] </li></ul>
    40. 40. More Customization <ul><li>jerry.save(:keywords => 'LOW_PRIORITY', </li></ul><ul><li>:pre_sql => '/*Pre comment*/', </li></ul><ul><li>:post_sql => </li></ul><ul><li> &quot;/*#{__FILE__} #{__LINE__}*/&quot;) </li></ul><ul><li>/*Pre comment*/ UPDATE LOW_PRIORITY `animals` </li></ul><ul><li>SET `created_at` = '2009-03-15 06:13:48', </li></ul><ul><li>`species_id` = NULL, `email` = NULL, </li></ul><ul><li>`password` = 'frenchfry', </li></ul><ul><li>`updated_at` = '2009-03-15 06:45:38', </li></ul><ul><li>`name` = 'Jerry G' </li></ul><ul><li>WHERE `id` = 7 /*animal_controller.rb 147 */ </li></ul>
    41. 41. Import (Bulk Insert) <ul><li>Instead of one-by-one, insert a ton of records fast </li></ul>
    42. 42. Import (Bulk Insert) <ul><li>Standard way: Insert each animal one by one </li></ul><ul><li>Animal.create!(:name => 'dolly dolphin', </li></ul><ul><li> :password => 'dolly') </li></ul><ul><li>Animal.create!(:name => 'big birdie', </li></ul><ul><li>:password => 'birdie') </li></ul><ul><li>and so on…. </li></ul>
    43. 43. Fast Import: One INSERT <ul><li>animals = [ Animal.new(:name => 'dolly dolphin', </li></ul><ul><li>:password => 'dolly'), </li></ul><ul><li>Animal.new(:name => 'big birdie', </li></ul><ul><li>:password => 'birdie')] </li></ul><ul><li>Animal.import animals </li></ul><ul><li>INSERT INTO `animals` </li></ul><ul><li>(`id`,`name`,`email`,`fav_beer`,`created_at`,`updated_at`,`password`) </li></ul><ul><li>VALUES </li></ul><ul><li>(NULL,'dolly dolphin',NULL,'Pabst', </li></ul><ul><li>'2009-03-20 00:17:15','2009-03-20 00:17:15','dolly'), </li></ul><ul><li>(NULL,'big birdie',NULL,'Pabst', </li></ul><ul><li>'2009-03-20 00:17:15','2009-03-20 00:17:15','birdie') </li></ul><ul><li>ON DUPLICATE KEY UPDATE `animals`.`updated_at`=VALUES(`updated_at`) </li></ul>
    44. 44. Fastest Import: fewer columns <ul><li>columns = [ :name, :password ] </li></ul><ul><li>values = [['dolly dolphin', 'dolly'], </li></ul><ul><li>['big birdie', 'birdie']] </li></ul><ul><li>options = { :validate => false, </li></ul><ul><li> :timestamps => false } </li></ul><ul><li>Animal.import columns, values, options </li></ul><ul><li>INSERT INTO `animals` (`name`,`password`) </li></ul><ul><li>VALUES </li></ul><ul><li>('dolly dolphin','dolly'),('big birdie','birdie') </li></ul>
    45. 45. Insert Select <ul><li>Standard: Query and Insert one by one </li></ul><ul><li>Species.find(:all).each do |s| </li></ul><ul><li>SpeciesZoo.create!(:species_id => s.id, </li></ul><ul><li> :zoo_id => zoo.id, </li></ul><ul><li> :extra_info => 'awesome') </li></ul><ul><li>end </li></ul><ul><li>Executes a query for each species </li></ul><ul><li>INSERT INTO `species_zoos` (`zoo_id`, `id`, `species_id`, `extra_info`) </li></ul><ul><li>VALUES (1, 3, 3, 'awesome') </li></ul><ul><li>INSERT INTO `species_zoos` (`zoo_id`, `id`, `species_id`, `extra_info`) </li></ul><ul><li>VALUES (1, 3, 2 , 'awesome') </li></ul><ul><li>And so on… </li></ul>
    46. 46. Insert Select Import <ul><li>SpeciesZoo.insert_select( </li></ul><ul><ul><li>:from => :species, </li></ul></ul><ul><ul><li>:select => ['species.id, ?', zoo], </li></ul></ul><ul><ul><li>:into => [:species_id, :zoo_id]) </li></ul></ul><ul><li>One INSERT statement </li></ul><ul><li>INSERT INTO `species_zoos` </li></ul><ul><li>( `species_id`, `zoo_id` ) </li></ul><ul><li>SELECT species.id, 1 FROM `species` </li></ul>
    47. 47. Temporary Tables <ul><li>Not so good for slave replication </li></ul><ul><li>Can be used as a sandbox then imported into a real table with ar-extensions gem </li></ul><ul><li>Animal. create_temporary_table do |t| </li></ul><ul><li>t.create!(:name => 'giraffe', </li></ul><ul><li>:password => 'goo') </li></ul><ul><li>Animal. insert_select ( </li></ul><ul><li>:from => t, </li></ul><ul><li>:select => [:name, :password, :fav_beer], </li></ul><ul><li>:into => [:name, :password, :fav_beer], </li></ul><ul><li> :on_duplicate_key_update => </li></ul><ul><li>[:password, :fav_beer]) </li></ul><ul><li>end </li></ul>
    48. 48. Customizing Find <ul><li>Additional finder options </li></ul><ul><li>:keywords </li></ul><ul><li>:pre_sql </li></ul><ul><li>:post_sql </li></ul><ul><li>:index_hint </li></ul>
    49. 49. Customizing Find <ul><li>Animal.find(:all, </li></ul><ul><li>:conditions => ['name = ?', 'Jerry G'], </li></ul><ul><li>:keywords => 'HIGH_PRIORITY', </li></ul><ul><li>:pre_sql => '/*Pre comment*/', </li></ul><ul><li>:post_sql => 'FOR UPDATE /*After the fact*/', </li></ul><ul><li>:index_hint => 'USE INDEX (uk_animal_name)' </li></ul><ul><li>) </li></ul><ul><li>/*Pre comment*/ SELECT HIGH_PRIORITY * </li></ul><ul><li>FROM `animals` USE INDEX (uk_animal_name) </li></ul><ul><li>WHERE (name = 'Jerry G') FOR UPDATE </li></ul><ul><li>/*After the fact*/ </li></ul>
    50. 50. Need more? Get dirty with find_by_sql <ul><li>sql = Animal.send :finder_sql_to_string, </li></ul><ul><li> :conditions => ['name = ?', 'Jerry G'] </li></ul><ul><li>sql.gsub! /WHERE/, 'where /* Dirty hand */' </li></ul><ul><li>Animal.find_by_sql sql </li></ul>
    51. 51. More: find_union & count_union <ul><li>Animal.find_union( </li></ul><ul><li>{:conditions => ['animals.name like ?', 'Jerry%']}, </li></ul><ul><li>{:conditions => ['species.name = ?', 'giraffe'], </li></ul><ul><li>:include => :species} </li></ul><ul><li>) </li></ul><ul><li>(SELECT `animals`.* FROM `animals` </li></ul><ul><li>WHERE (animals.name = 'Jerry G')) </li></ul><ul><li>UNION </li></ul><ul><li>(SELECT `animals`.* FROM `animals` </li></ul><ul><li>LEFT OUTER JOIN `species` ON </li></ul><ul><li>`species`.id = `animals`.species_id </li></ul><ul><li>WHERE (species.name = 'giraffe')) </li></ul>
    52. 52. Finder Issues: Speed and Memory <ul><li>paginate - less loaded into memory </li></ul><ul><li>:select option - Data is retrieved faster when fewer columns are selected </li></ul>
    53. 53. Paginated Finders <ul><li>Rails 2.3.2 includes :batch_size option </li></ul><ul><li>Animal.find_each(:batch_size => 2) do |animal| </li></ul><ul><li>#do something </li></ul><ul><li>end </li></ul><ul><li>Will Paginate Plugin </li></ul><ul><li>page = 1 </li></ul><ul><li>begin </li></ul><ul><li>animals = Animal.paginate :per_page => 2, :page => page </li></ul><ul><li>animals.each{|animal| …do something… } </li></ul><ul><li>end while (page = animals.next_page) </li></ul><ul><li>Paginating Find Plugin </li></ul>
    54. 54. :select <ul><li>Data is retrieved faster when fewer columns are selected </li></ul><ul><li>Animal.find :first, :select => 'name' </li></ul>
    55. 55. :include hates :select <ul><li>But :select is ignored with eager loading (:include)! </li></ul><ul><li>Animal.find :first, </li></ul><ul><li>:select => 'animals.name, species.name', </li></ul><ul><li>:include => :species, </li></ul><ul><li>:conditions => ['species.name like ?', 'giraffe'] </li></ul><ul><li>SELECT `animals`.`id` AS t0_r0, </li></ul><ul><li>`animals`.`name` AS t0_r1, </li></ul><ul><li>`animals`.`email` AS t0_r2, </li></ul><ul><li>`animals`.`fav_beer` AS t0_r3, </li></ul><ul><li>`animals`.`created_at` AS t0_r4, </li></ul><ul><li>`animals`.`updated_at` AS t0_r5, </li></ul><ul><li>`animals`.`password` AS t0_r6, </li></ul><ul><li>`animals`.`species_id` AS t0_r7, </li></ul><ul><li>`species`.`id` AS t1_r0, </li></ul><ul><li>`species`.`name` AS t1_r1 </li></ul><ul><li>FROM `animals` LEFT OUTER JOIN `species` ON </li></ul><ul><li>`species`.id = `animals`.species_id </li></ul><ul><li>WHERE (species.name like 'giraffe') LIMIT 1 </li></ul>
    56. 56. Alternatives to Eager Loading <ul><li>Eager loading for sparse :include can be time consuming </li></ul><ul><li>Use :join instead of :include </li></ul><ul><li>Eager Load Plugin </li></ul><ul><li>Rails 2.3.2 Query Cache helps </li></ul><ul><li>ActiveRecordContext Plugin </li></ul><ul><li>PiggyBack Plugin </li></ul>
    57. 57. :join instead of :include <ul><li>Eager loading is slow in Rails and can be slow on the </li></ul><ul><li>database. </li></ul><ul><li>Use an (inner) :join instead of :include </li></ul><ul><li>animal = Animal.find :first, </li></ul><ul><li>:select => 'animals.name, species.name as spec_name', </li></ul><ul><li>:joins => :species, </li></ul><ul><li>:conditions => ['species.name like ?', 'giraffe'] </li></ul><ul><li>animal.spec_name == 'giraffe' </li></ul>
    58. 58. Force it with Eager loading plugins <ul><li>Eager loading is slow in Rails and can be slow on the </li></ul><ul><li>database. </li></ul><ul><li>eload -select plugin </li></ul>
    59. 59. Help from Rails 2 Query Cache <ul><li>Animals of the same species are only loaded once </li></ul><ul><li>ActiveRecord::Base.cache { </li></ul><ul><li>Animal.find(:all).each {|a| a.species } </li></ul><ul><li>} </li></ul><ul><li>Animal Load (1.8ms) SELECT * FROM `animals` </li></ul><ul><li>Species Load (0.3ms) SELECT * FROM `species` WHERE (`species`.`id` = 2) </li></ul><ul><li>CACHE (0.0ms) SELECT * FROM `species` WHERE (`species`.`id` = 2) </li></ul><ul><li>Species Load (0.3ms) SELECT * FROM `species` WHERE (`species`.`id` = 1) </li></ul><ul><li>CACHE (0.0ms) SELECT * FROM `species` WHERE (`species`.`id` = 1) </li></ul>
    60. 60. ActiveRecordContext Plugin <ul><li>ActiveRecord::Base.with_context do </li></ul><ul><li>animals = Animal.find :all </li></ul><ul><li>Species.prefetch animals </li></ul><ul><li>animals.each {|a| a.species } </li></ul><ul><li>end </li></ul><ul><li>Animal Load (0.4ms) SELECT * FROM `animals` </li></ul><ul><li>[Context] Storing Animal records: 1, 2, 3, 4, 5, 6, and 7 </li></ul><ul><li>Species Load (0.4ms) SELECT * FROM `species` WHERE (`species`.`id` IN( 2,1 )) </li></ul><ul><li>[Context] Storing Species records: 2 and 1 </li></ul><ul><li>[Context] Found Species #2 </li></ul><ul><li>[Context] Found Species #2 </li></ul><ul><li>[Context] Found Species #1 </li></ul><ul><li>[Context] Found Species #1 </li></ul>
    61. 61. Piggyback Plugin <ul><li>Delegate records with :has_one and :belongs_to associations </li></ul><ul><li>Great for accessing extension tables with TEXT or BLOB </li></ul>
    62. 62. Piggyback Plugin <ul><li>Uses joins to delegate records from :has_one and :belongs_to </li></ul><ul><li>associations </li></ul><ul><li>class Animal < ActiveRecord::Base </li></ul><ul><li>belongs_to :species </li></ul><ul><li>piggy_back :species_name, :from => :species, </li></ul><ul><li>:attributes => [:name] </li></ul><ul><li>end </li></ul><ul><li>animal = Animal.find :first, :piggy => :species_name </li></ul><ul><li>animal.species_name == 'giraffe' </li></ul><ul><li>SELECT animals.*, species.name AS species_name </li></ul><ul><li>FROM `animals` LEFT JOIN species ON </li></ul><ul><li>species.id=animals.species_id LIMIT 1 </li></ul>
    63. 63. Avoiding Deadlock <ul><li>Deadlock Retry plugin - retries query up to 3 times </li></ul><ul><li>Batch Operations (AR-Extension plugin) </li></ul><ul><li>Animal.delete_all(['name like ?','giraffe%'], </li></ul><ul><li> :batch_size => 50) </li></ul>
    64. 64. Reading from a Replica <ul><li>Masochism plugin </li></ul>
    65. 65. Export Data <ul><li>ar-fixtures: Export entire table to yaml </li></ul><ul><li>ar-extensions: export to csv </li></ul><ul><li>ar-dumper: export (paginated) to yaml, xml or csv </li></ul>
    66. 66. Cache! Show me the money! <ul><li>Query Cache </li></ul><ul><li>Memcache </li></ul><ul><li>Static Record Cache plugin </li></ul>
    67. 67. ActiveRecord plugins and gems <ul><li>AR-Extensions http://www. continuousthinking .com/tags/ arext </li></ul><ul><li>Piggy Back http:// railsexpress .de/ svn / plugins /piggy_back/trunk/README </li></ul><ul><li>Eager Loading http://www. snowgiraffe .com/tech/?p=329 </li></ul><ul><li>Active Record Context http:// svn .techno-weenie.net/projects/ plugins /active_record_context/ </li></ul><ul><li>Will Paginate http:// github .com/ mislav /will_paginate/tree/master </li></ul><ul><li>Deadlock Retry http:// agilewebdevelopment .com/ plugins /deadlock_retry </li></ul><ul><li>Paginating Find http://www. railslodge .com/ plugins /287-paginating-find </li></ul><ul><li>Static Record Cache http:// github .com/ blythedunham /static_record_cache/tree/master </li></ul>
    68. 68. Other Useful Plugins <ul><li>Migration Plugins </li></ul><ul><li>Redhills Consulting Foreign Key Migrations http:// agilewebdevelopment .com/ plugins /foreign_key_migrations </li></ul><ul><li>Enum Column http:// enum -column. rubyforge .org/ </li></ul><ul><li>MySQL Migration Optimizer http://github.com/blythedunham/mysql_migration_optimizer/tree/master </li></ul><ul><li>Replica Plugins </li></ul><ul><li>Masochism http://github.com/technoweenie/masochism/tree/master </li></ul><ul><li>Active Delegate http://www. robbyonrails .com/articles/2007/10/05/multiple-database-connections-in-ruby-on-rails </li></ul><ul><li>Export Plugins </li></ul><ul><li>Ar-fixtures http://github.com/topfunky/ar_fixtures/tree/master </li></ul><ul><li>Ar-dumper http://github.com/blythedunham/ar_dumper/tree/master </li></ul>
    69. 69. Questions? <ul><li>Thanks for attending! I hope you are on the next level. </li></ul><ul><li>Slides available at: http://snowgiraffe.com </li></ul>
    70. 70. Pics <ul><li>Biz card: http://www.twozdai.com/ </li></ul><ul><li>Toolbox: http://www.flickr.com/photos/29802022@N07/2971774302/ </li></ul><ul><li>Cash: http://www. flickr .com/photos/ gnerk /2466566500/ </li></ul><ul><li>Emoo cow: http://blog.petegraham.co.uk/2007/05/15/animals-with-emo-haircuts/ </li></ul><ul><li>Giraffe in Car: http://www. norrishallstudio .com/assets/ img /products/ switchplates /Giraffe_in_Car_SO.jpg </li></ul><ul><li>Leafcutters: http://www.makingthelink.co.uk/Leafcutter%20Ants%20Corcovado.jpg </li></ul><ul><li>Puppy: http://www. flickr .com/photos/ todorrovic /2287792473/ </li></ul><ul><li>Dump truck: http://farm3.static.flickr.com/2065/2197925262_bd2726c3fa.jpg?v=0 </li></ul><ul><li>Ignore sign: http://www.flickr.com/photos/alexsuarez/2504638107/ </li></ul><ul><li>Tigers: http://www.flickr.com/photos/sharynmorrow/19981568/ </li></ul><ul><li>Single Leafcutter: http://www.flickr.com/photos/mattblucas/2176783448/ </li></ul><ul><li>Giraffe with tongue: http://www.flickr.com/photos/ucumari/2570608134/ </li></ul><ul><li>Giraffe in a can: http://www.flickr.com/photos/10159247@N04/2877489356/ </li></ul><ul><li>Beaver: http://www.flickr.com/photos/krrrista/2286455954/ </li></ul><ul><li>Mini Pig: http://www.richardaustinimages.com/ </li></ul><ul><li>Giraffe herd: http://www.flickr.com/photos/acastellano/2260928018/ </li></ul><ul><li>Giraffe skin: http://www.flickr.com/photos/tomorrowstand/1806095442 </li></ul><ul><li>Dirty hands: http://www.flickr.com/photos/dragonhide/2372544373/ </li></ul><ul><li>Blythe twins: http://www.flickr.com/photos/petitewanderlings/434252916/ </li></ul><ul><li>Dead lock: http://www.flickr.com/photos/fpsurgeon/2453544236/ </li></ul><ul><li>Foreign Keys: http://www.flickr.com/photos/zeeny79/347753999/ </li></ul><ul><li>Gorilla: http://www.flickr.com/photos/shuttershrink/425613091/ </li></ul><ul><li>Speed of light: http://www.flickr.com/photos/laserstars/908946494/ </li></ul><ul><li>Fat Giraffe: http://funnies.com/funny-picture-fat-giraffe.htm </li></ul>
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×