Come eliminare un valore di tipo enum in postgres?


109

Come posso eliminare un valore di tipo enum che ho creato in postgresql?

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

Ad esempio, voglio rimuovere moderatordall'elenco.

Non riesco a trovare nulla sui documenti.

Sto usando Postgresql 9.3.4.


4
drop type admin_level1?
bereal

1
La regola pratica: per ogni create xxxc'è undrop xxx
a_horse_with_no_name

IMO la risposta selezionata deve essere cambiata in un'altra.
Roman Podlinov

Risposte:


180

Elimina (rilascia) i tipi di enumerazione come qualsiasi altro tipo, con DROP TYPE:

DROP TYPE admin_level1;

È possibile che tu stia effettivamente chiedendo come rimuovere un singolo valore da un tipo di enumerazione ? Se è così, non puoi. Non è supportato :

Sebbene i enumtipi siano destinati principalmente a set di valori statici, è disponibile il supporto per l'aggiunta di nuovi valori a un tipo enum esistente e per la ridenominazione dei valori (vedere ALTER TYPE). I valori esistenti non possono essere rimossi da un tipo enum, né è possibile modificare l'ordinamento di tali valori, a meno di eliminare e ricreare il tipo enum.

È necessario creare un nuovo tipo senza il valore, convertire tutti gli usi esistenti del vecchio tipo per utilizzare il nuovo tipo, quindi eliminare il vecchio tipo.

Per esempio

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
È brillante! Con questo sono riuscito a risolvere il problema della migrazione alambicco. Non ho potuto aggiungere un nuovo tipo di (psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
enumerazione a

aggiungi disable_ddl_transaction! all'inizio del file di migrazione.
chell

ELIMINA DA blah WHERE power = 'god'; non funziona nel mio caso
ankit

1
TBH Non capisco perché sia ​​stata selezionata questa risposta. Questa risposta non è corretta! È possibile eliminare il valore da pg_enum con l'etichetta specificata.
Roman Podlinov

2
@RomanPoelinov manipolazione diretta del catalogo lo farei a proprio rischio. Ci sono motivi per cui postgres non supporta l'eliminazione nativa dei valori enum. In che modo questo "non corretto" rispetto a un hack di catalogo non supportato e non sicuro?
Craig Ringer

41

Molto ben scritto qui:

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

rinominare il tipo esistente

ALTER TYPE status_enum RENAME TO status_enum_old;

crea il nuovo tipo

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

aggiorna le colonne per utilizzare il nuovo tipo

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

rimuovere il vecchio tipo

DROP TYPE status_enum_old;

Questo collegamento ora restituisce un 503.
Oliver Evans il

32

Se vuoi eliminare un elemento di tipo enum, devi operare sulla tabella di sistema di PostgreSQL.

Con questo comando è possibile visualizzare tutti gli elementi enum type.

SELEZIONA * DA pg_enum;

Quindi controlla che il valore cercato sia unico. Per aumentare l'unicità durante la rimozione di rekoru deve essere passato 'enumtypid' oltre a 'enumlabel'.

Questo comando rimuove la voce nel tipo enum, dove "unico" è il tuo valore.

DELETE FROM pg_enum it WHERE en.enumtypid = 124 AND en.enumlabel = 'unique';

NOTA L'esempio che ho descritto deve essere usato, quando per caso aggiungiamo un nuovo valore al tipo enum, eppure non lo abbiamo usato da nessuna parte nel database.


20
Questa è un'operazione molto pericolosa , ma è molto rapida e concisa rimuovere un valore da un tipo di enumerazione se sai cosa stai facendo. Innanzitutto, assicurati che nessuna tabella utilizzi il valore enum che desideri rimuovere. Se non lo fai, ERROR: invalid internal value for enum
romperai

5
Esatto, questo è l'aspetto più importante che dovrebbe essere preso in considerazione. L'esempio che ho descritto deve essere utilizzato, quando per caso aggiungiamo nuovo valore a enum type, eppure non lo abbiamo utilizzato da nessuna parte nel database.
elcudro

1
Dato quanto è pericoloso questo comando, DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';la NOTA dovrebbe essere in GRASSETTO, non il comando. Se hai utilizzato il valore in qualche tabella, non puoi ripristinarlo. Non è possibile aggiornare le righe che contengono il valore, non è possibile convertire. L'unico modo è eliminare l'intera riga.
Sylvain

8

Per coloro che desiderano modificare i valori di enum, ricrearlo sembra essere l'unica soluzione praticabile e sicura.

Consiste nel convertire temporaneamente la colonna enum in un formato stringa, ricreare l'enum e quindi riconvertire la colonna stringa nel tipo enum.

Ecco un esempio:

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;dovrebbe essereALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_schema.your_column::your_enum_name;
Manuel Darveau

7

Utilizzare la seguente query per eliminare il valore ENUM dal tipo Postgresql

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

Solo informazioni per il tipo e il valore

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

È necessario modificare i valori esistenti in altro. Per questo, se è necessario aggiungere un nuovo valore, utilizzare:

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

Prima di eliminare, aggiornare il valore del tipo al nuovo valore del tipo o al valore esistente.


L'unico problema è che il typname in pg_type è minuscolo. quindi non funziona, a meno che non si utilizzi il enum_type minuscolo in SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd

2

Il modo programmatico per farlo è il seguente. Gli stessi passaggi generali forniti in https://stackoverflow.com/a/47305844/629272 sono appropriati, ma quelli sono più manuali di quanto non abbia senso per i miei scopi (scrivere un alambicco verso la migrazione). my_type,, my_type_olde value_to_delete, ovviamente, dovrebbero essere modificati come appropriato.

  1. Rinomina il tuo tipo.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Crea un nuovo tipo con i valori del tuo vecchio tipo, escludendo quello che desideri eliminare.

    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. Cambia tutte le colonne esistenti che utilizzano il vecchio tipo per utilizzare quello nuovo.

    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. Elimina il vecchio tipo.

    DROP TYPE my_type_old;

0

se il tuo set di dati non è così grande puoi eseguire il dump con --column-insertsmodificare il dump con un editor di testo, rimuovere il valore e reimportare il dump


0

Ha avuto lo stesso problema nella v.10. Postgres. L'eliminazione richiede determinate procedure e se la sequenza non è corretta, ci sarà persino la possibilità che la tabella venga bloccata per la lettura.

Ha scritto un comodo script da eliminare. Già dimostrato più volte le sue prestazioni. Tuttavia, questa procedura prevede la sostituzione del valore cancellato con uno nuovo (può essere NULL se il campo della tabella lo consente).

Per usarlo, devi solo inserire 3 valori.

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. Voce di elenco

-1

Non è possibile eliminare un singolo valore da ENUM, l'unica soluzione possibile è DROP e ricreare ENUM con i valori necessari.


È molto possibile, quello che probabilmente intendevi è "non ufficialmente supportato".
Rikudou_Sennin

@Rikudou_Sennin ti dispiacerebbe fornire un codice che può rimuovere un valore esatto da ENUM?
Zaytsev Dmitry

2
@ZaytsevDmitry eccoti qui:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.