How to delete an enum type value in postgres?

前端 未结 9 2015
没有蜡笔的小新
没有蜡笔的小新 2020-12-04 08:02

How do I delete an enum type value that I created in postgresql?

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

相关标签:
9条回答
  • 2020-12-04 08:33

    Had the same problem in v.10. postgres. Deletion requires certain procedures, and if the sequence is not correct, then there will even be a chance of the table being locked for reading.

    Wrote a convenient script to delete. Already proven several times its performance. However, this procedure involves replacing the deleted value with a new one (it can be NULL if the table field allows this).

    To use, you just need to fill in 3 values.

    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. List item
    0 讨论(0)
  • 2020-12-04 08:37

    For those who wish to modify the enum values, recreating it seems to be the only viable and safe solution.

    It consists in temporarely convert the enum column to a string format, recreate the enum and then reconverting the string column back to the enum type.

    Here is an example:

    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');
    
    0 讨论(0)
  • 2020-12-04 08:38

    The programmatic way to do this is as follows. The same general steps as given in https://stackoverflow.com/a/47305844/629272 are appropriate, but those are more manual than made sense for my purposes (writing an alembic down migration). my_type, my_type_old, and value_to_delete, should, of course, be changed as appropriate.

    1. Rename your type.

      ALTER TYPE my_type RENAME TO my_type_old;
      
    2. Create a new type with the values from your old type, excluding the one you want to delete.

      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. Change all existing columns which use the old type to use the new one.

      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. Delete the old type.

      DROP TYPE my_type_old;
      
    0 讨论(0)
  • 2020-12-04 08:42

    Very well written here:

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

    rename the existing type

    ALTER TYPE status_enum RENAME TO status_enum_old;
    

    create the new type

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

    update the columns to use the new type

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

    remove the old type

    DROP TYPE status_enum_old;
    

    Possible Errors and Troubleshooting:

    • invalid input value for enum {enum name}: "{some value}" - One or more rows have a value ("{some value}") that is not in your new type. You must handle these rows before you can update the column type.
    • default for column "{column_name}" cannot be cast automatically to type {enum_name} - The default value for the column is not in your new type. You must change or remove the default value for the column before you can update the column type. Thanks to Philipp for this addition.
    0 讨论(0)
  • 2020-12-04 08:44

    Use following query to Delete ENUM value from Postgresql type

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

    Just info for what's type and what's value

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

    You should change existing values to other. For that if you need to add new value, then use:

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

    Before deleting, update type value to new type value or existing value.

    0 讨论(0)
  • 2020-12-04 08:45

    It's not possible to delete individual value from ENUM, the only possible solution is to DROP and recreate ENUM with needed values.

    0 讨论(0)
提交回复
热议问题