Wie lösche ich einen Aufzählungstypwert in Postgres?

108

Wie lösche ich einen Aufzählungstypwert, den ich in postgresql erstellt habe?

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

ZB möchte ich entfernen moderator von der Liste .

Ich kann anscheinend nichts in den Dokumenten finden.

Ich verwende Postgresql 9.3.4.

Amjith
quelle
4
drop type admin_level1?
Bereal
1
Die Faustregel: Für jeden create xxxgibt es eindrop xxx
a_horse_with_no_name
IMO muss die ausgewählte Antwort in eine andere geändert werden.
Roman Podlinov

Antworten:

180

Sie löschen (löschen) Aufzählungstypen wie jeden anderen Typ mit DROP TYPE:

DROP TYPE admin_level1;

Ist es möglich, dass Sie tatsächlich fragen, wie Sie einen einzelnen Wert aus einem Aufzählungstyp entfernen können ? Wenn ja, können Sie nicht. Es wird nicht unterstützt :

Obwohl enumTypen hauptsächlich für statische Wertesätze vorgesehen sind, wird das Hinzufügen neuer Werte zu einem vorhandenen Aufzählungstyp und das Umbenennen von Werten unterstützt (siehe ALTER TYPE). Bestehende Werte können weder aus einem Aufzählungstyp entfernt noch die Sortierreihenfolge solcher Werte geändert werden, ohne den Aufzählungstyp zu löschen und neu zu erstellen.

Sie müssen einen neuen Typ ohne den Wert erstellen, alle vorhandenen Verwendungen des alten Typs in den neuen Typ konvertieren und dann den alten Typ löschen.

Z.B

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;
Craig Ringer
quelle
1
Das ist brilliant! Damit gelang es mir, das Migrationsproblem von Alembic zu lösen. Ich konnte keinen neuen Aufzählungstyp hinzufügen, weil(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
Karantan
füge disable_ddl_transaction hinzu! an den Anfang der Migrationsdatei.
Chell
LÖSCHEN VON BLA WO Macht = 'Gott'; funktioniert in meinem Fall nicht
Ankit
1
TBH Ich verstehe nicht, warum diese Antwort ausgewählt wurde. Diese Antwort ist nicht richtig! Sie können den Wert mit der angegebenen Bezeichnung aus pg_enum löschen.
Roman Podlinov
2
@RomanPoelinov direkte Katalogmanipulation Ich würde auf eigenes Risiko gehen. Es gibt Gründe, warum Postgres das native Löschen von Enum-Werten nicht unterstützt. Wie ist dies "nicht korrekt" im Vergleich zu einem nicht unterstützten und unsicheren Katalog-Hack?
Craig Ringer
41

Sehr gut hier geschrieben:

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

Benennen Sie den vorhandenen Typ um

ALTER TYPE status_enum RENAME TO status_enum_old;

Erstellen Sie den neuen Typ

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

Aktualisieren Sie die Spalten, um den neuen Typ zu verwenden

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

Entfernen Sie den alten Typ

DROP TYPE status_enum_old;
dnaik
quelle
Dieser Link gibt jetzt eine 503 zurück.
Oliver Evans
30

Wenn Sie ein Element vom Typ Aufzählung löschen möchten, müssen Sie die Systemtabelle von PostgreSQL bearbeiten.

Mit diesem Befehl können Sie alle Aufzählungstypen anzeigen.

SELECT * FROM pg_enum;

Überprüfen Sie dann, ob der gesuchte Wert eindeutig ist. Um die Eindeutigkeit beim Entfernen von Rekoru zu erhöhen, muss zusätzlich zu 'enumlabel' 'enumtypid' übergeben werden.

Dieser Befehl entfernt den Eintrag im Aufzählungstyp, wobei 'unique' Ihr Wert ist.

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

HINWEIS Das von mir beschriebene Beispiel muss verwendet werden, wenn wir dem Aufzählungstyp zufällig einen neuen Wert hinzufügen und ihn dennoch nirgendwo in der Datenbank verwendet haben.

elcudro
quelle
20
Dies ist eine sehr gefährliche Operation , aber es ist sehr schnell und prägnant, einen Wert aus einem Aufzählungstyp zu entfernen, wenn Sie wissen, was Sie tun. Stellen Sie zunächst sicher, dass keine Tabelle den Enum-Wert verwendet, den Sie entfernen möchten. Wenn Sie nicht, Sie schlecht brechen alle Tabellen, die den ENUM - Wert verweisen (zB die Auswahl aus einer solchen Tabelle zurückkehren ERROR: invalid internal value for enumund ergeben NO Ergebnisse.)
Clint Pachl
5
Das ist richtig, dies ist der wichtigste Aspekt, der berücksichtigt werden sollte. Das Beispiel, das ich beschrieben habe, muss verwendet werden, wenn wir dem Aufzählungstyp zufällig einen neuen Wert hinzufügen und ihn dennoch nirgendwo in der Datenbank verwendet haben.
Elcudro
1
Angesichts der Gefährlichkeit dieses Befehls DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';sollte der HINWEIS fett gedruckt sein, nicht der Befehl. Wenn Sie den Wert in einer Tabelle verwendet haben, können Sie ihn nicht wiederherstellen. Sie können die Zeilen, die den Wert enthalten, nicht aktualisieren und nicht konvertieren. Die einzige Möglichkeit besteht darin, die gesamte Zeile zu löschen.
Sylvain
8

Für diejenigen, die die Enum-Werte ändern möchten, scheint die Neuerstellung die einzig praktikable und sichere Lösung zu sein.

Es besteht darin, die Aufzählungsspalte vorübergehend in ein Zeichenfolgenformat zu konvertieren, die Aufzählung neu zu erstellen und die Zeichenfolgenspalte dann wieder in den Aufzählungstyp umzuwandeln.

Hier ist ein Beispiel:

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');
sveilleux2
quelle
ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;sollte seinALTER 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
6

Verwenden Sie die folgende Abfrage, um den ENUM-Wert aus dem Postgresql-Typ zu löschen

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

Nur Infos für welchen Typ und welchen Wert

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

Sie sollten vorhandene Werte in andere ändern. Wenn Sie dafür einen neuen Wert hinzufügen müssen, verwenden Sie:

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

Aktualisieren Sie vor dem Löschen den Typwert auf einen neuen Typwert oder einen vorhandenen Wert.

Somnath Muluk
quelle
Das einzige Problem ist, dass der Typname in pg_type in Kleinbuchstaben geschrieben ist. es funktioniert also nicht, es sei denn, Sie verwenden den Kleinbuchstaben enum_type in SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd
2

Der programmatische Weg, dies zu tun, ist wie folgt. Die gleichen allgemeinen Schritte wie unter https://stackoverflow.com/a/47305844/629272 sind angemessen, aber diese sind für meine Zwecke eher manuell als sinnvoll (Schreiben einer Alembic-Down-Migration). my_type,, my_type_oldund value_to_deletesollte natürlich entsprechend geändert werden.

  1. Benennen Sie Ihren Typ um.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Erstellen Sie einen neuen Typ mit den Werten Ihres alten Typs, mit Ausnahme desjenigen, den Sie löschen möchten.

    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. Ändern Sie alle vorhandenen Spalten, die den alten Typ verwenden, um den neuen zu verwenden.

    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. Löschen Sie den alten Typ.

    DROP TYPE my_type_old;
kalifornisch
quelle
0

Wenn Ihr Dataset nicht so groß ist, können Sie --column-insertsden Speicherauszug mit einem Texteditor bearbeiten, den Wert entfernen und den Speicherauszug erneut importieren

Sherpya
quelle
0

Hatte das gleiche Problem in v.10. postgres. Das Löschen erfordert bestimmte Verfahren, und wenn die Reihenfolge nicht korrekt ist, besteht sogar die Möglichkeit, dass die Tabelle zum Lesen gesperrt wird.

Schrieb ein praktisches Skript zum Löschen. Bereits mehrfach seine Leistung bewiesen. Bei diesem Verfahren wird jedoch der gelöschte Wert durch einen neuen ersetzt (er kann NULL sein, wenn das Tabellenfeld dies zulässt).

Zur Verwendung müssen Sie nur 3 Werte eingeben.

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. Listenpunkt
SergioBazileyroElMurdoMendez
quelle
-1

Es ist nicht möglich, einzelne Werte aus ENUM zu löschen. Die einzig mögliche Lösung besteht darin, ENUM mit den erforderlichen Werten zu TROPFEN und neu zu erstellen.

Zaytsev Dmitry
quelle
Es ist sehr wahrscheinlich, dass Sie wahrscheinlich "nicht offiziell unterstützt" gemeint haben.
Rikudou_Sennin
@Rikudou_Sennin Würde es Ihnen etwas ausmachen, einen Code bereitzustellen, mit dem ein exakter Wert aus ENUM entfernt werden kann?
Zaytsev Dmitry
2
@ ZaytsevDmitry hier sind Sie:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov