Comment supprimer une valeur de type enum dans postgres?

109

Comment supprimer une valeur de type enum que j'ai créée dans postgresql?

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

Par exemple, je souhaite supprimer moderatorde la liste.

Je n'arrive pas à trouver quoi que ce soit sur la documentation.

J'utilise Postgresql 9.3.4.

Amjith
la source
4
drop type admin_level1?
bereal
1
La règle de base: pour chaque create xxxil y a undrop xxx
a_horse_with_no_name
OMI, la réponse sélectionnée doit être remplacée par une autre.
Roman Podlinov

Réponses:

180

Vous supprimez (supprimez) les types d'énumérations comme tout autre type, avec DROP TYPE:

DROP TYPE admin_level1;

Est-il possible que vous vous demandiez comment supprimer une valeur individuelle d'un type enum ? Si oui, vous ne pouvez pas. Ce n'est pas pris en charge :

Bien que les enumtypes soient principalement destinés aux ensembles de valeurs statiques, il existe une prise en charge pour ajouter de nouvelles valeurs à un type enum existant et pour renommer les valeurs (voir ALTER TYPE). Les valeurs existantes ne peuvent pas être supprimées d'un type enum, ni l'ordre de tri de ces valeurs ne peut être modifié, à moins de supprimer et de recréer le type enum.

Vous devez créer un nouveau type sans la valeur, convertir toutes les utilisations existantes de l'ancien type pour utiliser le nouveau type, puis supprimer l'ancien type.

Par exemple

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
la source
1
C'est génial! Avec cela, j'ai réussi à résoudre le problème de migration d'Alembic. Je n'ai pas pu ajouter un nouveau type d'énumération à cause de(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan
ajoutez disable_ddl_transaction! en haut du fichier de migration.
chell le
SUPPRIMER DE BLAH WHERE power = 'god'; ne fonctionne pas dans mon cas
ankit
1
TBH Je ne comprends pas pourquoi cette réponse a été choisie. Cette réponse n'est pas correcte! Vous pouvez supprimer la valeur de pg_enum avec l'étiquette spécifiée.
Roman Podlinov
2
@RomanPoelinov manipulation directe du catalogue, je le ferais à vos risques et périls. Il y a des raisons pour lesquelles postgres ne prend pas en charge la suppression native des valeurs d'énumération. En quoi est-ce "incorrect" par rapport à un piratage de catalogue non pris en charge et dangereux?
Craig Ringer
41

Très bien écrit ici:

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

renommer le type existant

ALTER TYPE status_enum RENAME TO status_enum_old;

créer le nouveau type

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

mettre à jour les colonnes pour utiliser le nouveau type

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

supprimer l'ancien type

DROP TYPE status_enum_old;
dnaik
la source
Ce lien renvoie maintenant un 503.
Oliver Evans
32

Si vous voulez supprimer un élément de type enum, vous devez opérer sur la table système de PostgreSQL.

Avec cette commande, vous pouvez afficher tous les éléments de type énumération.

SELECT * FROM pg_enum;

Vérifiez ensuite que la valeur recherchée est unique. Pour augmenter l'unicité lors de la suppression de rekoru, il faut passer «enumtypid» en plus de «enumlabel».

Cette commande supprime l'entrée de type enum, où «unique» est votre valeur.

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

REMARQUE L'exemple que j'ai décrit doit être utilisé, quand par hasard nous ajoutons une nouvelle valeur au type enum, et pourtant nous ne l'avons utilisé nulle part dans la base de données.

elcudro
la source
20
C'est une opération très dangereuse , mais c'est très rapide et succinct de supprimer une valeur d'un type enum si vous savez ce que vous faites. Tout d'abord, assurez-vous qu'aucune table n'utilise la valeur d'énumération que vous souhaitez supprimer. Si vous ne le faites pas, vous casserez mal toutes les tables qui référencent la valeur d'énumération (par exemple, la sélection dans une telle table retournera ERROR: invalid internal value for enumet ne donnera AUCUN résultat.)
Clint Pachl
5
C'est vrai, c'est l'aspect le plus important à prendre en compte. L'exemple que j'ai décrit doit être utilisé, lorsque par hasard nous ajoutons une nouvelle valeur au type enum, et pourtant nous ne l'avons utilisé nulle part dans la base de données.
elcudro
1
Compte tenu de la dangerosité de cette commande, DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';la NOTE doit être en gras, pas la commande. Si vous avez utilisé la valeur dans une table, vous ne pouvez pas en récupérer. Vous ne pouvez pas mettre à jour les lignes qui contiennent la valeur, vous ne pouvez pas convertir. Le seul moyen est de supprimer la ligne entière.
Sylvain
8

Pour ceux qui souhaitent modifier les valeurs d'énumération, la recréer semble être la seule solution viable et sûre.

Il consiste à convertir temporairement la colonne enum en un format de chaîne, à recréer l'énumération puis à reconvertir la colonne de chaîne au type enum.

Voici un exemple:

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
la source
ALTER TABLE your_schema.your_table ALTER COLUMN your_column TYPE your_schema.your_enum_name USING your_enum_name::your_schema.your_column;devrait êtreALTER 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

Utilisez la requête suivante pour supprimer la valeur ENUM du type PostgreSQL

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

Juste des informations sur le type et la valeur

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

Vous devez changer les valeurs existantes en autre. Pour cela, si vous avez besoin d'ajouter une nouvelle valeur, utilisez:

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

Avant de supprimer, mettez à jour la valeur du type avec une nouvelle valeur de type ou une valeur existante.

Somnath Muluk
la source
Le seul problème est que le typname dans pg_type est en minuscules. donc ça ne marche pas, à moins d'utiliser les minuscules enum_type dans SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd
2

La manière programmatique de le faire est la suivante. Les mêmes étapes générales que celles indiquées dans https://stackoverflow.com/a/47305844/629272 sont appropriées, mais elles sont plus manuelles que sensées pour mes besoins (écrire une migration vers le bas d'alambic). my_type, my_type_oldet value_to_delete, devrait, bien entendu, être modifié le cas échéant.

  1. Renommez votre type.

    ALTER TYPE my_type RENAME TO my_type_old;
  2. Créez un nouveau type avec les valeurs de votre ancien type, à l'exclusion de celui que vous souhaitez supprimer.

    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. Modifiez toutes les colonnes existantes qui utilisent l'ancien type pour utiliser le nouveau.

    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. Supprimez l'ancien type.

    DROP TYPE my_type_old;
californien
la source
0

si votre jeu de données n'est pas si grand, vous pouvez effectuer un vidage en --column-insertséditant le vidage avec un éditeur de texte, en supprimant la valeur et en réimportant le vidage

sherpya
la source
0

Eu le même problème dans la v.10. postgres. La suppression nécessite certaines procédures, et si la séquence n'est pas correcte, il y aura même une chance que la table soit verrouillée pour la lecture.

Écrit un script pratique à supprimer. Déjà prouvé plusieurs fois ses performances. Cependant, cette procédure implique de remplacer la valeur supprimée par une nouvelle (elle peut être NULL si le champ de table le permet).

Pour l'utiliser, il vous suffit de renseigner 3 valeurs.

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. Élément de liste
SergioBazileyroElMurdoMendez
la source
-1

Il n'est pas possible de supprimer une valeur individuelle d'ENUM, la seule solution possible est de DROP et de recréer ENUM avec les valeurs nécessaires.

Zaytsev Dmitry
la source
C'est très possible, ce que vous vouliez probablement dire n'est «pas officiellement pris en charge».
Rikudou_Sennin
@Rikudou_Sennin pourriez-vous fournir un code qui peut supprimer une valeur exacte d'ENUM?
Zaytsev Dmitry
2
@ZaytsevDmitry vous voilà:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov