我想将列类型从int
更改为uuid
。我正在使用以下声明
ALTER TABLE tableA ALTER COLUMN colA SET DATA TYPE UUID;
但我收到错误消息
ERROR: column "colA" cannot be cast automatically to type uuid
HINT: Specify a USING expression to perform the conversion.
我很困惑如何使用USING
进行演员表。
答案 0 :(得分:38)
你不能只将int4强制转换为uuid;它是一个无效的uuid,只设置32位,高96位为零。
如果要生成新的UUID以完全替换整数,并且如果没有对这些整数的现有外键引用,则可以使用实际生成新值的伪转换。
如果没有备份数据,请不要运行。它会永久丢弃colA
中的旧值。
ALTER TABLE tableA ALTER COLUMN colA SET DATA TYPE UUID USING (uuid_generate_v4());
更好的方法通常是添加一个uuid列,然后修复任何指向它的外键引用,最后删除原始列。
您需要安装UUID模块:
CREATE EXTENSION "uuid-ossp";
报价很重要。
答案 1 :(得分:5)
我必须从文本转换为uuid类型,以及从Django迁移,所以在解决这个问题后,我在http://baltaks.com/2015/08/how-to-change-text-fields-to-a-real-uuid-type-for-django-and-postgresql写了一下,以防万一。相同的技术适用于整数到uuid的转换。
根据评论,我在这里添加了完整的解决方案:
Django很可能会为您创建一个类似于:
的迁移class Migration(migrations.Migration):
dependencies = [
('app', '0001_auto'),
]
operations = [
migrations.AlterField(
model_name='modelname',
name='uuid',
field=models.UUIDField(db_index=True, unique=True),
),
]
首先,将自动创建的迁移操作作为state_operations
参数放入RunSQL操作中。这允许您提供自定义迁移,但让Django了解数据库模式发生了什么。
class Migration(migrations.Migration):
dependencies = [
('app', '0001_auto'),
]
operations = [
migrations.RunSQL(sql_commands, None, [
migrations.AlterField(
model_name='modelname',
name='uuid',
field=models.UUIDField(db_index=True, unique=True),
),
]),
]
现在,您需要为该sql_commands
变量提供一些SQL命令。我选择将sql放入一个单独的文件中,然后使用以下python代码加载:
sql_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '0001.sql')
with open(sql_path, "r") as sqlfile:
sql_commands = sqlfile.read()
现在是真正棘手的部分,我们实际执行迁移。您想要的基本命令如下:
alter table tablename alter column uuid type uuid using uuid::uuid;
但我们在这里的原因是因为索引。正如我发现的那样,Django喜欢在运行测试时使用迁移在字段上创建随机命名的索引,因此如果只删除然后重新创建一个或两个固定名称索引,测试将失败。所以以下是在转换为uuid字段之前将删除文本字段上的一个约束和所有索引的sql。它也可以一次用于多个表。
DO $$
DECLARE
table_names text[];
this_table_name text;
the_constraint_name text;
index_names record;
BEGIN
SELECT array['table1',
'table2'
]
INTO table_names;
FOREACH this_table_name IN array table_names
LOOP
RAISE notice 'migrating table %', this_table_name;
SELECT CONSTRAINT_NAME INTO the_constraint_name
FROM information_schema.constraint_column_usage
WHERE CONSTRAINT_SCHEMA = current_schema()
AND COLUMN_NAME IN ('uuid')
AND TABLE_NAME = this_table_name
GROUP BY CONSTRAINT_NAME
HAVING count(*) = 1;
if the_constraint_name is not NULL then
RAISE notice 'alter table % drop constraint %',
this_table_name,
the_constraint_name;
execute 'alter table ' || this_table_name
|| ' drop constraint ' || the_constraint_name;
end if;
FOR index_names IN
(SELECT i.relname AS index_name
FROM pg_class t,
pg_class i,
pg_index ix,
pg_attribute a
WHERE t.oid = ix.indrelid
AND i.oid = ix.indexrelid
AND a.attrelid = t.oid
AND a.attnum = any(ix.indkey)
AND t.relkind = 'r'
AND a.attname = 'uuid'
AND t.relname = this_table_name
ORDER BY t.relname,
i.relname)
LOOP
RAISE notice 'drop index %', quote_ident(index_names.index_name);
EXECUTE 'drop index ' || quote_ident(index_names.index_name);
END LOOP; -- index_names
RAISE notice 'alter table % alter column uuid type uuid using uuid::uuid;',
this_table_name;
execute 'alter table ' || quote_ident(this_table_name)
|| ' alter column uuid type uuid using uuid::uuid;';
RAISE notice 'CREATE UNIQUE INDEX %_uuid ON % (uuid);',
this_table_name, this_table_name;
execute 'create unique index ' || this_table_name || '_uuid on '
|| this_table_name || '(uuid);';
END LOOP; -- table_names
END;
$$
答案 2 :(得分:3)
如果有人遇到这个老话题。我通过首先将字段更改为CHAR类型然后更改为UUID类型来解决问题。
答案 3 :(得分:2)
我在很长一段时间后碰到这个,但有一种方法可以将整数列转换为具有某种向后兼容性的UUID,即保留一种方法来引用旧值,而不是放弃你的价值观。 它包括将整数值转换为十六进制字符串,然后用必要的零填充它以组成一个人工UUID。
因此,假设您当前的整数列名为ColA,以下语句将执行此操作(请注意using
部分):
ALTER TABLE tableA ALTER COLUMN ColA SET DATA TYPE UUID USING LPAD(TO_HEX(ColA), 32, '0')::UUID;
答案 4 :(得分:2)
我可以使用以下过程来转换INT
类型的列,并使用SERIAL
速记将其配置为递增主键:
-- Ensure the UUID extension is installed.
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Dropping and recreating the default column value is required because
-- the default INT value is not compatible with the new column type.
ALTER TABLE table_to_alter ALTER COLUMN table_id DROP DEFAULT,
ALTER COLUMN table_id SET DATA TYPE UUID USING (uuid_generate_v4()),
ALTER COLUMN table_id SET DEFAULT uuid_generate_v4();
答案 5 :(得分:1)
在PostgreSQL 9.3中,你可以这样做:
ALTER TABLE "tableA" ALTER COLUMN "ColA" SET DATA TYPE UUID USING "ColA"::UUID;
并将数据类型转换为UUID
,这样可以避免错误消息。
答案 6 :(得分:0)
警告:我注意到一些注释和答案,试图将整数转换为UUID4。
您不得投射或强制设置uuid值。它们必须使用与RFC4122相关的函数来生成。
UUID必须随机分布,否则将不起作用。您无法转换或输入自己的UUID,因为它们不会正确分发。这可能导致不良行为者猜测您的排序或在您的UUID中找到其他工件或模式,从而导致他们发现其他对象。
任何将答案转换为char类型然后转换为uuid的答案都可能导致这些类型的问题。
在此处遵循任何有关“ uuid_generate_v4 ”的答案。忽略那些不使用形式函数的强制转换或设置。