Пакетная обработка при использовании ActiveRecord::Base.connection.execute

Я занят написанием миграции, которая позволит нам перевести наш ямлер с Syck на Psych и, наконец, обновить наш проект до ruby ​​2. Эта миграция будет очень ресурсоемкой, хотя мне нужно будет использовать чанкинг.

Я написал следующий метод, чтобы подтвердить, что результат миграции, которую я планирую использовать, дает ожидаемый результат и может быть выполнен без простоев. Чтобы активная запись не выполняла сериализацию автоматически, мне нужно было использовать ActiveRecord::Base.connection.execute

Мой метод, который описывает преобразование следующим образом

 def show_summary(table, column_name)
  a = ActiveRecord::Base.connection.execute <<-SQL
   SELECT id, #{column_name} FROM #{table}
  SQL
  all_rows = a.to_a; ""
  problem_rows = all_rows.select do |row|
    original_string = Syck.dump(Syck.load(row[1]))
    orginal_object = Syck.load(original_string)

    new_string = Psych.dump(orginal_object)
    new_object = Syck.load(new_string)

    Syck.dump(new_object) != original_string rescue true
  end

problem_rows.map do |row|
  old_string = Syck.dump(Syck.load(row[1]))
  new_string = Psych.dump(Syck.load(old_string)) rescue "Parse failure"
  roundtrip_string = begin
    Syck.dump(Syck.load(new_string))
  rescue => e
    e.message
  end

  new_row = {}
  new_row[:id] = row[0]
  new_row[:original_encoding] = old_string
  new_row[:new_encoding] = roundtrip_string
  new_row
  end
end

Как вы можете использовать дозирование при использовании ActiveRecord::Base.connection.execute?

Для полноты, моя функция обновления выглядит следующим образом

  # Migrate the given serialized YAML column from Syck to Psych
  # (if any).
  def migrate_to_psych(table, column)
    table_name = ActiveRecord::Base.connection.quote_table_name(table)

    column_name = ActiveRecord::Base.connection.quote_column_name(column)

    fetch_data(table_name, column_name).each do |row|
      transformed = ::Psych.dump(convert(Syck.load(row[column])))

      ActiveRecord::Base.connection.execute <<-SQL
         UPDATE #{table_name}
         SET #{column_name} = #{ActiveRecord::Base.connection.quote(transformed)}
         WHERE id = #{row['id']};
      SQL
    end
  end

  def fetch_data(table_name, column_name)
    ActiveRecord::Base.connection.select_all <<-SQL
       SELECT id, #{column_name}
       FROM #{table_name}
       WHERE #{column_name} LIKE '---%'
    SQL
  end

Который я получил от http://fossies.org/linux/openproject/db/migrate/migration_utils/legacy_yamler.rb

1 ответ

Решение

Вы можете легко построить что-то с помощью SQL LIMIT а также OFFSET статьи:

def fetch_data(table_name, column_name)
  batch_size, offset = 1000, 0
  begin
    batch = ActiveRecord::Base.connection.select_all <<-SQL
      SELECT id, #{column_name}
      FROM #{table_name}
      WHERE #{column_name} LIKE '---%'
      LIMIT #{batch_size} 
      OFFSET #{offset}
    SQL
    batch.each do |row|
      yield row
    end
    offset += batch_size
  end until batch.empty?
end

который вы можете использовать почти так же, как и раньше, только без .each:

fetch_data(table_name, column_name) do |row| ... end

НТН!

Другие вопросы по тегам