我有一个数据库我想转换为使用UUID作为postgresql中的主键。
我有大约30个具有深层多级关联的表。是否有一种“简单”的方法将所有当前ID转换为UUID?
从这个:https://coderwall.com/p/n_0awq,我可以看到我可以在迁移中改变表格。我在想这样的事情:
for client in Client.all
# Retrieve children
underwritings = client.underwritings
# Change primary key
execute 'ALTER TABLE clients ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE clients ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
# Get new id - is this already generated?
client_id = client.id
for underwriting in underwritings
locations = underwriting.locations
other_record = underwriting.other_records...
execute 'ALTER TABLE underwritings ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE underwritings ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
underwriting.client_id = client_id
underwriting.saved
underwriting_id = underwriting.id
for location in locations
buildings = location.buildings
execute 'ALTER TABLE locations ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE locations ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
location.undewriting_id = underwriting_id
location.save
location_id = location.id
for building in buildings
...
end
end
for other_record in other_records
...
end
...
...
end
end
问题:
非常感谢您提供帮助或提示。
答案 0 :(得分:9)
我觉得这些很乏味。可以对PostgreSQL使用直接查询来转换表与现有数据。
主键:
ALTER TABLE students
ALTER COLUMN id DROP DEFAULT,
ALTER COLUMN id SET DATA TYPE UUID USING (uuid(lpad(replace(text(id),'-',''), 32, '0'))),
ALTER COLUMN id SET DEFAULT uuid_generate_v4()
其他参考资料:
ALTER TABLE students
ALTER COLUMN city_id SET DATA TYPE UUID USING (uuid(lpad(replace(text(city_id),'-',''), 32, '0')))
上面左边用零填充整数值并转换为UUID。这种方法不需要id映射,如果需要,可以检索旧id。
由于没有数据复制,这种方法非常快。
要处理这些更复杂的多态关联案例,请使用https://github.com/kreatio-sw/webdack-uuid_migration。此gem为ActiveRecord :: Migration添加了额外的帮助程序,以简化这些迁移。
答案 1 :(得分:4)
我认为通过Rails尝试做这样的事情只会使问题复杂化。我完全忽略了Rails方面的事情,只是在SQL中做。
您的第一步是获取数据库的完整备份。然后将该备份还原到另一个数据库中:
首先,您需要通过添加真正的外键来匹配所有Rails关联来清理数据。你的一些FK很有可能会失败,如果他们这样做,你将不得不清理破损的参考文献。
现在您已拥有干净的数据rename all your tables,以便为新的UUID版本腾出空间。对于表t
,我们将重命名的表称为t_tmp
。对于每个t_tmp
,创建另一个表来保存从旧integer
id
到新UUID id
的映射,如下所示:
create table t_id_map (
old_id integer not null,
new_id uuid not null default uuid_generate_v1()
)
然后填充它:
insert into t_id_map (old_id)
select id from t_tmp
当你在这里时,你可能希望索引t_id_map.old_id
。
这为我们提供了带有integer
ID的旧表和每个t_tmp
的查找表,用于将旧id
映射到新的integer
。
现在使用UUID创建新表,替换保留serial
s的所有旧id
和insert into ... select ... from
列;我也会在这一点上添加真正的外键;你应该对你的数据产生偏执:破碎的代码是暂时的,破碎的数据通常是永远的。
此时填充新表非常简单:只需使用t_id_map
构造并加入相应的id
表,将旧的t_tmp
映射到新的t_id_map
。一旦数据被映射和复制,您将需要进行一些健全性检查以确保一切仍然有意义。然后,您可以放弃{{1}}和{{1}}表,继续您的生活。
在数据库的副本上练习该过程,编写脚本,然后离开。
您当然希望在执行此项工作时关闭访问数据库的所有应用程序。
答案 2 :(得分:1)
不想添加外键,并希望使用rails迁移。无论如何,如果其他人想要这样做,我就会这样做(例如2张桌子,总共做了32张):
def change
execute 'CREATE EXTENSION "uuid-ossp";'
execute <<-SQL
ALTER TABLE buildings ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL;
ALTER TABLE buildings ALTER COLUMN guid SET DEFAULT uuid_generate_v1();
ALTER TABLE buildings ADD COLUMN location_guid uuid;
ALTER TABLE clients ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL;
ALTER TABLE clients ALTER COLUMN guid SET DEFAULT uuid_generate_v1();
ALTER TABLE clients ADD COLUMN agency_guid uuid;
ALTER TABLE clients ADD COLUMN account_executive_guid uuid;
ALTER TABLE clients ADD COLUMN account_representative_guid uuid;
SQL
for record in Building.all
location = record.location
record.location_guid = location.guid
record.save
end
for record in Client.all
agency = record.agency
record.agency_guid = agency.guid
account_executive = record.account_executive
record.account_executive_guid = account_executive.guid unless account_executive.blank?
account_representative = record.account_representative
record.account_representative_guid = account_representative.guid unless account_representative.blank?
record.save
end
execute <<-SQL
ALTER TABLE buildings DROP CONSTRAINT buildings_pkey;
ALTER TABLE buildings DROP COLUMN id;
ALTER TABLE buildings RENAME COLUMN guid TO id;
ALTER TABLE buildings ADD PRIMARY KEY (id);
ALTER TABLE buildings DROP COLUMN location_id;
ALTER TABLE buildings RENAME COLUMN location_guid TO location_id;
ALTER TABLE clients DROP CONSTRAINT clients_pkey;
ALTER TABLE clients DROP COLUMN id;
ALTER TABLE clients RENAME COLUMN guid TO id;
ALTER TABLE clients ADD PRIMARY KEY (id);
ALTER TABLE clients DROP COLUMN agency_id;
ALTER TABLE clients RENAME COLUMN agency_guid TO agency_id;
ALTER TABLE clients DROP COLUMN account_executive_id;
ALTER TABLE clients RENAME COLUMN account_executive_guid TO account_executive_id;
ALTER TABLE clients DROP COLUMN account_representative_id;
ALTER TABLE clients RENAME COLUMN account_representative_guid TO account_representative_id;
SQL
end