Як видалити значення типу enum у postgres?


109

Як видалити значення типу enum, яке я створив у postgresql?

create type admin_level1 as enum('classifier', 'moderator', 'god');

Наприклад, я хочу видалити moderatorзі списку.

Я не можу знайти нічого в документах.

Я використовую Postgresql 9.3.4.


4
drop type admin_level1?
bereal

1
Правило: для кожного create xxxєdrop xxx
a_horse_with_no_name

IMO вибрану відповідь потрібно змінити на іншу.
Роман Підлінов

Відповіді:


180

Ви видаляєте (видаляєте) типи перерахунків, як і будь-який інший тип, за допомогою DROP TYPE:

DROP TYPE admin_level1;

Чи можливо, ви насправді запитуєте про те, як видалити окреме значення з типу enum ? Якщо так, ви не можете. Не підтримується :

Хоча enumтипи призначені насамперед для статичних наборів значень, існує підтримка додавання нових значень до існуючого типу enum та перейменування значень (див. ALTER TYPE). Існуючі значення не можуть бути вилучені з типу enum, а також не можна змінювати порядок сортування таких значень, окрім випадання та повторного створення типу enum.

Ви повинні створити новий тип без значення, перетворити всі існуючі вживання старого типу для використання нового типу, а потім скинути старий тип.

Напр

CREATE TYPE admin_level1 AS ENUM ('classifier', 'moderator');

CREATE TABLE blah (
    user_id integer primary key,
    power admin_level1 not null
);

INSERT INTO blah(user_id, power) VALUES (1, 'moderator'), (10, 'classifier');

ALTER TYPE admin_level1 ADD VALUE 'god';

INSERT INTO blah(user_id, power) VALUES (42, 'god');

-- .... oops, maybe that was a bad idea

CREATE TYPE admin_level1_new AS ENUM ('classifier', 'moderator');

-- Remove values that won't be compatible with new definition
-- You don't have to delete, you might update instead
DELETE FROM blah WHERE power = 'god';

-- Convert to new type, casting via text representation
ALTER TABLE blah 
  ALTER COLUMN power TYPE admin_level1_new 
    USING (power::text::admin_level1_new);

-- and swap the types
DROP TYPE admin_level1;

ALTER TYPE admin_level1_new RENAME TO admin_level1;

1
Це геніально! Цим я встиг вирішити проблему міграції алембів. Я не зміг додати новий тип enum через(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan

додати відключити_ddl_transaction! вгорі файлу міграції.
чел

ВІДКЛЮЧИТИ ВІД БЛАГО, де влада = 'бог'; не працює в моєму випадку
ankit

1
TBH Я не розумію, чому саме цю відповідь було обрано. Ця відповідь не правильна! Ви можете видалити значення з pg_enum із вказаною міткою.
Роман Підлінов

2
@ РоманПоелінов прямий каталог маніпулювання я б на свій страх і ризик. Існують причини, що postgres не підтримує видалення значень enum спочатку. Як це "не правильно" в порівнянні з непідтримуваним і небезпечним злом каталогу?
Крейг Рінгер

41

Тут дуже добре написано:

http://blog.yo1.dog/updating-enum-values-in-postgresql-the-safe-and-easy-way/

перейменувати існуючий тип

ALTER TYPE status_enum RENAME TO status_enum_old;

створити новий тип

CREATE TYPE status_enum AS ENUM('queued', 'running', 'done');

оновіть стовпці, щоб використовувати новий тип

ALTER TABLE job ALTER COLUMN job_status TYPE status_enum USING job_status::text::status_enum;

видалити старий тип

DROP TYPE status_enum_old;

Зараз це посилання повертає 503.
Олівер Еванс

32

Якщо ви хочете видалити елемент типу enum, вам потрібно працювати з системною таблицею PostgreSQL.

За допомогою цієї команди ви можете відобразити всі елементи перерахунку елементів.

ВИБІР * ВІД pg_enum;

Потім перевірте, чи шукається значення унікальне. Щоб збільшити унікальність під час видалення rekoru, слід передати "enumtypid" на додаток до "enumlabel".

Ця команда видаляє запис типу enum, де "унікальним" є ваше значення.

ВИДАЛИТИ З pg_enum en WHERE en.enumtypid = 124 І en.enumlabel = 'унікальний';

ПРИМІТКА Приклад, який я описав, повинен бути використаний, коли випадково ми додаємо нове значення типу enum, але ми ще не використовували його ніде в базі даних.


20
Це дуже небезпечна операція , але це дуже швидко і швидко видалити значення з типу enum, якщо ви знаєте, що ви робите. По-перше, переконайтесь, що жодна таблиця не використовує значення enum, яке ви хочете видалити. Якщо ви цього не зробите, ви погано зламаєте всі таблиці, на які посилається значення перерахунку (наприклад, вибравши таку таблицю, повернеться ERROR: invalid internal value for enumі дасть результати НІ .)
Клінт Пахл,

5
Правильно, це найважливіший аспект, який слід враховувати. Приклад, який я описав, повинен бути використаний, коли випадково ми додаємо нове значення типу enum, але ми ще не використовували його ніде в базі даних.
elcudro

1
З огляду на те, наскільки небезпечна ця команда, DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';ПРИМІТКА повинна бути в BOLD, а не в команді. Якщо ви використали значення в якійсь таблиці, ви не можете відновити її. Ви не можете оновити рядки, що містять значення, ви не можете конвертувати. Єдиний спосіб - видалити весь рядок.
Sylvain

8

Для тих, хто хоче змінити значення перерахунків, відтворення це здається єдиним життєздатним і безпечним рішенням.

Він полягає у тимчасовому перетворенні стовпця enum у формат рядка, відтворенні enum та повторному перетворенні стовпця рядків назад у тип enum.

Ось приклад:

ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE varchar(255);
ALTER TABLE your_schema.your_table ALTER COLUMN your_column SET DEFAULT('your_default_enum_value');
DROP TYPE your_schema.your_enum_name;
CREATE TYPE your_schema.your_enum_name AS ENUM ('enum1', 'enum2', 'enum3');
ALTER TABLE your_schema.your_table ALTER your_column DROP DEFAULT;
ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;
ALTER TABLE your_schema.your_table ALTER COLUMN your_column SET DEFAULT('your_default_enum_value');

ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;має бутиALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_schema.your_column::your_enum_name;
Мануель Дарво

7

Використовуйте наступний запит для видалення значення ENUM з типу Postgresql

DELETE FROM pg_enum
WHERE enumlabel = 'moderator'
AND enumtypid = ( SELECT oid FROM pg_type WHERE typname = 'admin_level1');

Просто інформація про тип і значення

DELETE FROM pg_enum
WHERE enumlabel = 'ENUM_VALUE'
AND enumtypid = ( SELECT oid FROM pg_type WHERE typname = 'ENUM_TYPE')

Ви повинні змінити існуючі значення на інші. Для цього, якщо вам потрібно додати нове значення, тоді використовуйте:

ALTER TYPE **ENUM_TYPE** ADD VALUE '**ENUM_VALUE2**'; 

Перед видаленням оновіть значення типу до нового типу або наявного значення.


Єдине питання - це друкарське ім'я в pg_type - це малі регістри. тож його не працює, якщо тільки не використовується нижній регістр enum_type in SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

Програмний спосіб зробити це наступним чином. Ті самі загальні кроки, що наведені в https://stackoverflow.com/a/47305844/629272, є відповідними, але вони є більш ручними, ніж мають сенс для моїх цілей (написання міграції внизу). my_type, my_type_oldі value_to_delete, звичайно, повинні бути змінені у відповідних випадках.

  1. Перейменуйте свій тип.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Створіть новий тип зі значеннями зі свого старого типу, виключаючи той, який потрібно видалити.

    DO $$
    BEGIN
        EXECUTE format(
            'CREATE TYPE my_type AS ENUM (%s)',
            (
                SELECT string_agg(quote_literal(value), ',')
                FROM unnest(enum_range(NULL::my_type_old)) value
                WHERE value <> 'value_to_delete'
            )
        );
    END $$;
  3. Змініть усі існуючі стовпці, які використовують старий тип, щоб використовувати новий.

    DO $$
    DECLARE
        column_data record;
        table_name varchar(255);
        column_name varchar(255);
    BEGIN
        FOR column_data IN
            SELECT cols.table_name, cols.column_name
                FROM information_schema.columns cols
                WHERE udt_name = 'my_type_old'
        LOOP
            table_name := column_data.table_name;
            column_name := column_data.column_name;
            EXECUTE format(
                '
                    ALTER TABLE %s
                    ALTER COLUMN %s
                    TYPE my_type
                    USING %s::text::my_type;
                ',
                table_name, column_name, column_name
            );
        END LOOP;
    END $$;
  4. Видалити старий тип.

    DROP TYPE my_type_old;

0

якщо ваш набір даних не такий великий, ви можете демпінгувати з --column-insertsредагуванням дампа за допомогою текстового редактора, видалити значення та повторно імпортувати дамп


0

Була така ж проблема в v.10. постгреси. Для видалення потрібні певні процедури, і якщо послідовність не є правильною, то навіть буде шанс блокування таблиці для читання.

Написав зручний сценарій для видалення. Вже кілька разів довів свою ефективність. Однак ця процедура включає заміну видаленого значення новим (воно може бути NULL, якщо поле таблиці дозволяє це).

Для використання потрібно просто заповнити 3 значення.

DO $$
DECLARE
    enumTypeName VARCHAR := 'enum_name'; -- VALUE #1, set yor value!
    enumOldFieldValue varchar := 'old_enum_value'; -- VALUE #2, enum value which have to be deleted
    enumNewFieldValue varchar := null; -- VALUE #3, which new value must be instead of deleted
    sql varchar:='';
    rec record;
BEGIN
    raise info 'Check on old and new enum values.';
    IF exists(select * FROM pg_enum -- check existing of OLD enum value
              WHERE enumtypid = (select oid from pg_type where typName=cast(enumTypeName as varchar) limit 1) and enumlabel=cast(enumOldFieldValue as varchar))
      AND
       (exists(select *
               FROM pg_enum -- check existing of NEW enum value
               WHERE enumtypid = (select oid from pg_type where typName = cast(enumTypeName as varchar) limit 1)
                 and enumlabel = cast(enumNewFieldValue as varchar))
           OR
        enumNewFieldValue IS NULL)
        THEN
            raise info 'Check passed!';

            -- selecting all tables with schemas which has column with enum relation
            create temporary table tmp_table_names
             as SELECT concat(c.table_schema,'.',c.table_name ) as table_name, c.column_name
                FROM information_schema.columns c
                WHERE c.udt_name = cast(enumTypeName as varchar)
                  and c.table_schema=c.udt_schema and data_type = 'USER-DEFINED';

            -- if we have table(s) that uses such enum
            if exists(select * from tmp_table_names)
                then
                    FOR rec in (select table_name, column_name from tmp_table_names) LOOP
                        sql:= format('UPDATE %1$s set %2$s = %3$L where %2$s=%4$L',rec.table_name, rec.column_name, enumNewFieldValue, enumOldFieldValue);
                        raise info 'Update by looping: %', sql;
                        EXECUTE sql;
                    END LOOP;
            end if;

            -- just after changing all old values in all tables we can delete old enum value
            sql := format('DELETE FROM pg_enum WHERE enumtypid = (select oid from pg_type where typName=%1$L limit 1) and enumlabel=%2$L',enumTypeName,enumOldFieldValue);
            raise info 'Delete enum value: %', sql;
            EXECUTE sql;

            drop table  tmp_table_names;
        ELSE
            raise info 'Old or new enum values is missing.';
    end if;
END $$;
  1. Елемент списку

-1

Видалити окреме значення з ENUM неможливо, єдине можливе рішення - DROP та відтворити ENUM з необхідними значеннями.


Дуже можливо, те, що ви, мабуть, мали на увазі, це "офіційно не підтримується".
Rikudou_Sennin

@Rikudou_Sennin Ви не хочете вказати код, який може видалити одне точне значення з ENUM?
Зайцев Дмитро

2
@ZaytsevDmitry ви тут:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Роман Подлинов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.