ALTER TABLE mit Rails 3 und MySQL

Da nicht immer von Anfang an die Datenbank- bzw. Tabellenstruktur eindeutig ist und geänderte Anforderungen das Datenbankschema beeinflussen, müssen Anpassungen vorgenommen werden. Dies ist in der Regel einfach in den Migration-Scripten konfigurierbar. Mit dem vorgestellten Commit wird es in Zukunft in einer optimierteren Version als Bulk ablaufen.

Der erwähnte Commit ist im GitHub einsehbar (https://github.com/rails/rails/commit/30176f28a41681c7607eed39d03501327869d40c).

 

Das Problem, das bisher bei Änderungen an Tabellen bestand, war die Art wie MySQL diese ausführt. Wenn man von folgendem Beispiel ausgeht:
Es sollen zwei Spalten hinzugefügt werden und der Datentyp einer weiteren soll geändert werden.

[ruby]
change_table(:users) do |t|
t.string :im_handle
t.belongs_to :company
t.change :birthdate, :datetime
end
[/ruby]

 

Diese Migration führte bisher zu drei separat ausgeführten Befehlen:

 

[bash]
ALTER TABLE `users` ADD `im_handle` varchar(255)
ALTER TABLE `users` ADD `company_id` int(11)
ALTER TABLE `users` CHANGE `updated_at` `updated_at` datetime DEFAULT NULL
[/bash]

 

Diese Aktion ist bei kleinen Datenbanken „relativ“ unproblematisch, aber führt zu Problemen bei größeren.Denn wenn man einen Blick in die MySQL Dokumentation wirft, wird man feststellen, dass MySQL einiges im Hintergrund ausführt, um die Änderung durchzuführen. Die grobe Reihenfolge sieht ungefähr so aus:

 
  1. Die Tabelle wird gesperrt (lock). Kein UPDATE und INSERT mehr zugelassen
  2. Eine temporäre Kopie der Tabelle samt Inhalt wird angelegt
  3. Die gewünschten Änderungen werden auf die Kopie angewendet
  4. Die originale Tabelle wird durch die geänderte Kopie ersetzt
 

Wenn nun das oben gezeigte Beispiel ausgeführt wird, würden diese Schritte drei mal ausgeführt werden. Das ist nicht unbedingt performant und nicht immer gewünscht.

In den kommenden Rails-Versionen kann dieser Vorgang als Bulk durchgeführt werden. Dadurch werden alle Änderungen als einziger Befehl durchgeführt:

[ruby]
change_table(:users, :bulk => true) do |t|
t.string :im_handle
t.belongs_to :company
t.change :birthdate, :datetime
end
[/ruby]

Durch den zusätzlichen Parameter „bulk => true“ werden die einzelnen Statements zu einer zusammengefasst:

[bash]
ALTER TABLE `users` ADD COLUMN `im_handle` varchar(255), ADD COLUMN `company_id` int(11), CHANGE `updated_at` `updated_at` datetime DEFAULT NULL
[/bash]

Dadurch werden Datenbankänderungen in Zukunft nicht nur schneller sondern auch atomarer.

Screencast: Seed Data

Rails-Migration-Scripte sind sehr hilfreich bei der Erstellung von DB-Tabellen und deren gleichzeitige Konfiguration. Allerdings es häufig auch nötig bei der Migration auch Daten einzutragen und/oder bestehende Daten anzupassen.

Ryan Bates zeigt in seinem Screencast wie man dies lösen kann.

Downloadlinks:
Download (13.5 MB, 7:56)
Alternativer download für iPod & Apple TV (9.3 MB, 7:56)

Weitere Ressourcen:

Quellcode:

[bash]
script/generate model operating_system name:string
script/generate model country name:string code:string
rake db:migrate
rake db:seed
[/bash]

[ruby]
# db/seeds.rb
require ‚open-uri‘
require ‚active_record/fixtures‘

["Windows", "Linux", "Mac OS X"].each do |os|
OperatingSystem.find_or_create_by_name(os)
end

Country.delete_all
open("http://openconcept.ca/sites/openconcept.ca/files/country_code_drupal_0.txt") do |countries|
countries.read.each_line do |country|
code, name = country.chomp.split("|")
Country.create!(:name => name, :code => code)
end
end

Fixtures.create_fixtures("#{Rails.root}/test/fixtures", "operating_systems")
[/ruby]