2015-09-30 19 views
7

Tôi cần chuyển đổi các trường string thành integer và sử dụng enum thay thế. Cách tốt nhất để làm điều này mà không làm mất dữ liệu là gì?Rails 4 di chuyển để thay đổi kiểu dữ liệu cột từ chuỗi thành số nguyên và sao lưu dữ liệu (postgres)

Đây là di cư hiện tại:

class CreateSystems < ActiveRecord::Migration 
    def change 
    create_table :systems do |t| 
     t.string :operation 
     t.string :status 

     t.timestamps null: false 
    end 
    end 
end 

Sau đó, tôi thay đổi loại của các lĩnh vực như sau:

class ChangeColumnsForSystems < ActiveRecord::Migration 
    def change 
    change_column :systems, :operation, :integer 
    change_column :systems, :status, :integer 
    end 
end 

Và cập nhật mô hình tập tin.

/app/models/system.rb

... 
enum operation { start: 0, stop: 1 } 
enum status { init: 0, working: 1, complete: 2 } 
... 

Làm thế nào tôi có thể cập nhật dữ liệu cũ?

Trả lời

7

Sau một số nghiên cứu tôi thấy đây là một giải pháp thích hợp.

class ChangeColumnsForSystems < ActiveRecord::Migration 
    def change 
    change_column :systems, :operation, "integer USING (CASE operation WHEN 'start' THEN '0'::integer ELSE '1'::integer END)", null: false 
    change_column :systems, :status, "integer USING (CASE status WHEN 'init' THEN '0'::integer WHEN 'working' THEN '1'::integer ELSE '2'::integer END)", null: false 
    end 
end 

UPDATE: Trong một số trường hợp, bạn sẽ phải loại bỏ giá trị mặc định trước khi thay đổi kiểu. Đây là phiên bản có tính năng quay lại.

class ChangeColumnsForSystems < ActiveRecord::Migration 
    def up 
    change_column_default :systems, :status, nil 
    change_column :systems, :operation, "integer USING (CASE operation WHEN 'start' THEN '0'::integer ELSE '1'::integer END)", null: false 
    change_column :systems, :status, "integer USING (CASE status WHEN 'init' THEN '0'::integer WHEN 'working' THEN '1'::integer ELSE '2'::integer END)", null: false, default: 0 
    end 

    def down 
    change_column_default :systems, :status, nil 
    change_column :systems, :operation, "varchar USING (CASE operation WHEN '0' THEN 'start'::varchar ELSE 'stop'::varchar END)", null: false 
    change_column :systems, :status, "varchar USING (CASE status WHEN '0' THEN 'init'::varchar WHEN '1' THEN 'working'::varchar ELSE 'complete'::varchar END)", null: false, default: 'init' 
    end 
end 
2

Bạn có thể làm điều đó trong 2 di cư bước

1. Đổi tên hiện tại operation cột và thêm mới với loại cần thiết

def up 
    rename_column :systems, :operation, :operation_str 
    add_column :systems, :operation, ... # your options 
end 

2. Di chuyển các giá trị từ cột cũ sang mới và thả cột cũ

def up 
    System.all.each do |sys| 
     sys.operation = sys.operation_str.to_i # replace it with your converter 
    end 
    remove_column :systems, :operation 
end 

Đừng quên viết mã rollback nếu cần thiết

+0

di cư này sẽ làm hỏng tất cả dữ liệu, bởi vì khi bạn gọi '.to_i' trên một chuỗi, bạn sẽ luôn có được 0. mảnh tiếp theo của mã sẽ làm việc như mong đợi. Tuy nhiên nó không phải là giải pháp tốt nhất 'sys.operation = System.operations [sys.operation_str]' – Pav31

Các vấn đề liên quan