Очень часто при разработке веб-приложения возникает необходимость внести изменения в структуру базы данных: добавить колонку, изменить ее тип, перенести значения из одной колонки в другую и т.п.
Для осуществления этих задач в веб-фреймворке Ruby on Rails существуют миграции.
$ rails g migration add_invoice_number_to_statements invoice_number:string{20}
Эта команда, выполненная в консоли, создаст файл миграции:
db/migrate/20150403113019_add_invoice_number_to_statements.rb
Предположим, что нам нужно, в колонку invoice_number еще поместить данные из другой колонки - invoice_id, имеющей тип int. Миграция может выглядеть так:
Вроде мы молодцы, в методе up написали условие if, благодаря которому не будет запроса на обновление поля invoice_number, если в поле invoice_id ничего нет, уменьшает общее число запросов к БД. Кроме того, мы использовали метод find_each, который загружает Statement пачками по 1000 штук, материализует их в объекты и обрабатывает. Это позволит нашей миграции не затратить много оперативной памяти. Какие мы молодцы!
Однако, это не так. Если в таблице statements 50 000 записей, то данная миграция приведет к 50 000 запросам UPDATE.
Иногда полезно миграцию писать на чистом добром SQL:
А теперь мы молодцы - миграция отработает молниеносно. А создание комментария к полю является актом творения добра и еще одним поводом для гордости.
Для осуществления этих задач в веб-фреймворке Ruby on Rails существуют миграции.
$ rails g migration add_invoice_number_to_statements invoice_number:string{20}
Эта команда, выполненная в консоли, создаст файл миграции:
db/migrate/20150403113019_add_invoice_number_to_statements.rb
class AddInvoiceNumberToStatements < ActiveRecord::Migration def change add_column :statements, :invoice_number, :string, :limit => 20 end end
Предположим, что нам нужно, в колонку invoice_number еще поместить данные из другой колонки - invoice_id, имеющей тип int. Миграция может выглядеть так:
class AddInvoiceNumberToStatements < ActiveRecord::Migration def up add_column :statements, :invoice_number, :string, :limit => 20 Statement.find_each do |statement| statement.update_column(:invoice_number, statement.invoice_id) if statement.invoice_id end end def down remove_column :statements, :invoice_number end end
Вроде мы молодцы, в методе up написали условие if, благодаря которому не будет запроса на обновление поля invoice_number, если в поле invoice_id ничего нет, уменьшает общее число запросов к БД. Кроме того, мы использовали метод find_each, который загружает Statement пачками по 1000 штук, материализует их в объекты и обрабатывает. Это позволит нашей миграции не затратить много оперативной памяти. Какие мы молодцы!
Однако, это не так. Если в таблице statements 50 000 записей, то данная миграция приведет к 50 000 запросам UPDATE.
Иногда полезно миграцию писать на чистом добром SQL:
class AddInvoiceNumberToStatementsAndMigrateData < ActiveRecord::Migration def up add_column :statements, :invoice_number, :string, :limit => 20 execute <<-SQL COMMENT ON COLUMN statements.invoice_number IS 'Номер счет-фактуры'; UPDATE statements SET invoice_number = invoice_id::text; SQL end def down remove_column :statements, :invoice_number end end
А теперь мы молодцы - миграция отработает молниеносно. А создание комментария к полю является актом творения добра и еще одним поводом для гордости.
Комментариев нет:
Отправить комментарий