我创建了一个以UUID为主键的模型Order
。
现在,我想将其更改回正常的增量整数。
我试图像以下一样进行迁移:
add_column :orders, :id, :primary_key
但它显然不起作用,因为已有id
列。
我发现了很多关于如何从id转到uuid的帖子,但没有找到如何扭转它的帖子。
答案 0 :(得分:4)
表格有数据吗?
如果不是,只需删除旧的id列并添加新的:
remove_column :orders, :id
add_column :orders, :id, :primary_key
如果是,请将id
列重命名为id_deprecated
rename_column :orders, :id, :id_deprecated
add_column :orders, :id, :primary_key
并添加您的逻辑以处理其他表中old_id的引用。
答案 1 :(得分:1)
这是在 Rails 应用程序中自动将所有 UUID 转换为整数 ID 的迁移。
有关进一步优化的代码,请参阅:github.com/westonganger/rails_uuid_to_integer_primary_keys
class ChangeUuidToInteger < ActiveRecord::Migration::Current
def change
### LOAD ALL MODELS for `.subclasses` method
Dir.glob(Rails.root.join("app/models/*.rb")).each{|f| require(f) }
id_map = {}
ApplicationRecord.subclasses.each do |outer_klass|
outer_klass.reset_column_information
if outer_klass.column_for_attribute(outer_klass.primary_key).type == :uuid
case outer_klass.connection.adapter_name
when "Mysql2"
execute "ALTER TABLE #{outer_klass.table_name} DROP PRIMARY KEY;"
else
result = outer_klass.connection.execute("
SELECT ('ALTER TABLE ' || table_schema || '.' || table_name || ' DROP CONSTRAINT ' || constraint_name) as my_query
FROM information_schema.table_constraints
WHERE table_name = '#{outer_klass.table_name}' AND constraint_type = 'PRIMARY KEY';")
sql_drop_constraint_command = result.values[0].first
execute(sql_drop_constraint_command)
end
rename_column outer_klass.table_name, outer_klass.primary_key, "tmp_old_#{outer_klass.primary_key}"
add_column outer_klass.table_name, outer_klass.primary_key, outer_klass.connection.native_database_types[:primary_key]
outer_klass.reset_column_information
records = outer_klass.all
if outer_klass.column_names.include?("created_at")
records = records.reorder(created_at: :asc)
end
id_map[outer_klass] = {}
records.each_with_index do |record, i|
old_id = record.send("tmp_old_#{outer_klass.primary_key}")
if record.send(outer_klass.primary_key).nil?
new_id = i+1
record.update_columns(outer_klass.primary_key => new_id)
else
new_id = record.send(outer_klass.primary_key)
end
id_map[outer_klass][old_id] = new_id
end
remove_column outer_klass.table_name, "tmp_old_#{outer_klass.primary_key}"
outer_klass.reset_column_information
end
end
ApplicationRecord.subclasses.each do |inner_klass|
inner_klass.reflect_on_all_associations(:belongs_to).each do |reflection|
if inner_klass.column_for_attribute(reflection.foreign_key).type == :uuid
if reflection.polymorphic?
### POLYMORPHIC BELONGS TO
#null_constraint = inner_klass.columns.find{|x| x.name == reflection.foreign_key }.null
if inner_klass.connection.index_exists?(inner_klass.table_name, reflection.foreign_key)
remove_index inner_klass.table_name, reflection.foreign_key
end
rename_column inner_klass.table_name, reflection.foreign_key, "tmp_old_#{reflection.foreign_key}"
add_column inner_klass.table_name, reflection.foreign_key, :bigint#, null: null_constraint
add_index inner_klass.table_name, reflection.foreign_key
inner_klass.reset_column_information
id_map.each do |outer_klass, inner_id_map|
records = inner_klass
.where("#{inner_klass.table_name}.tmp_old_#{reflection.foreign_key} IS NOT NULL")
.where("#{reflection.foreign_type}" => outer_klass.name)
records.each do |record|
old_id = record.send("tmp_old_#{reflection.foreign_key}")
if old_id
new_id = inner_id_map[old_id]
if new_id
### First Update Column ID Value
record.update_columns(reflection.foreign_key => new_id)
else
# Orphan record, we just clear the value
record.update_columns(reflection.foreign_key => nil)
end
end
end
end
### Then Change Column Type
remove_column inner_klass.table_name, "tmp_old_#{reflection.foreign_key}"
inner_klass.reset_column_information
elsif id_map[reflection.klass]
### DIRECT BELONGS TO
inner_id_map = id_map[reflection.klass]
#null_constraint = inner_klass.columns.find{|x| x.name == reflection.foreign_key }.null
if inner_klass.connection.index_exists?(inner_klass.table_name, reflection.foreign_key)
remove_index inner_klass.table_name, reflection.foreign_key
end
rename_column inner_klass.table_name, reflection.foreign_key, "tmp_old_#{reflection.foreign_key}"
add_column inner_klass.table_name, reflection.foreign_key, :bigint#, null: null_constraint
add_index inner_klass.table_name, reflection.foreign_key
inner_klass.reset_column_information
records = inner_klass.where("#{inner_klass.table_name}.tmp_old_#{reflection.foreign_key} IS NOT NULL")
records.each do |record|
old_id = record.send("tmp_old_#{reflection.foreign_key}")
if old_id
new_id = inner_id_map[old_id]
if new_id
### First Update Column ID Value
record.update_columns(reflection.foreign_key => new_id)
else
# Orphan record, we just clear the value
record.update_columns(reflection.foreign_key => nil)
end
end
end
### Then Change Column Type
remove_column inner_klass.table_name, "tmp_old_#{reflection.foreign_key}"
inner_klass.reset_column_information
end
end
end
inner_klass.reflect_on_all_associations(:has_and_belongs_to_many).each do |reflection|
if id_map[reflection.klass]
inner_id_map = id_map[reflection.klass]
#null_constraint = join_klass.columns.find{|x| x.name == reflection.foreign_key }.null
if inner_klass.connection.index_exists?(reflection.join_table, reflection.association_foreign_key)
remove_index reflect.join_table, reflection.association_foreign_key
end
rename_column reflect.join_table, reflection.association_foreign_key, "tmp_old_#{reflection.association_foreign_key}"
add_column reflect.join_table, reflection.association_foreign_key, :bigint
add_index reflect.join_table, reflection.association_foreign_key
inner_id_map.each do |old_id, new_id|
if new_id
### First Update Column ID Value
execute "UPDATE #{reflection.join_table} SET #{reflection.association_foreign_key} = '#{new_id}' WHERE tmp_old_#{reflection.association_foreign_key} = '#{old_id}'"
end
end
execute "DELETE FROM #{reflection.join_table} WHERE tmp_old_#{reflection.association_foreign_key} NOT IN ('#{inner_id_map.values.join("','")}')"
remove_column reflection.join_table, "tmp_old_#{reflection.association_foreign_key}"
#join_klass.reset_column_information
end
end
end
end
end