Подтвердить что ты не робот

Postgresql изменяет тип столбца от int до UUID

Я хотел бы изменить тип столбца с 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 для выполнения трансляции.

4b9b3361

Ответ 1

Вы не можете просто использовать 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";

Важные цитаты.

Ответ 2

Мне пришлось конвертировать из текста в тип 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),
        ),
    ]

Сначала поместите автоматически созданные операции миграции в операцию RunSQL в качестве параметра state_operations. Это позволяет вам выполнять пользовательскую миграцию, но держать 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 для этой переменной sql_commands. Я решил поместить 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 любит использовать ваши миграции для создания индексов со случайным именем на ваших полях во время выполнения тестов, поэтому ваши тесты не пройдут, если вы просто удалите, а затем воссоздадите фиксированный индекс имени или два. Таким образом, следующим является sql, который удалит одно ограничение и все индексы в текстовом поле перед преобразованием в поле uuid. Это также работает для нескольких таблиц за один раз.

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;
$$

Ответ 3

Просто, если кто-то сталкивается с этой старой темой. Я решил проблему, сначала изменив поле на тип CHAR, а затем на тип UUID.

Ответ 4

Я столкнулся с этим через долгое время, но есть способ преобразовать ваш целочисленный столбец в UUID с некоторой обратной совместимостью, а именно, сохранить способ иметь ссылку на ваши старые значения, а не отбрасывать ваши значения , Он состоит из преобразования целочисленного значения в шестнадцатеричную строку, а затем дополнения ее необходимыми нулями для создания искусственного UUID.

Итак, если предположить, что ваш текущий целочисленный столбец называется ColA, это сделает следующий оператор (обратите внимание на часть using):

ALTER TABLE tableA ALTER COLUMN ColA SET DATA TYPE UUID USING LPAD(TO_HEX(ColA), 32, '0')::UUID;

Ответ 5

В PostgreSQL 9.3 вы можете сделать это:

ALTER TABLE "tableA" ALTER COLUMN "ColA" SET DATA TYPE UUID USING "ColA"::UUID;

И введите тип данных в UUID, и это позволит избежать сообщения об ошибке.

Ответ 6

Мне удалось преобразовать столбец с типом 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();