如何在postgres中删除枚举类型值?


109

如何删除我在postgresql中创建的枚举类型值?

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

例如,我想moderator从列表中删除。

我似乎在文档上找不到任何内容。

我正在使用Postgresql 9.3.4。


4
drop type admin_level1
bereal 2014年

1
经验法则:每create xxx有一个drop xxx
a_horse_with_no_name

IMO必须将所选答案更改为另一个答案。
罗曼·波德利诺夫

Answers:


180

您可以像其他类型一样删除(删除)枚举类型DROP TYPE

DROP TYPE admin_level1;

您是否可能实际上在问如何从枚举类型中删除单个值?如果是这样,您就不能这样做。不支持

尽管enum类型主要用于静态值集,但支持将新值添加到现有的枚举类型并重命名值(请参阅参考资料ALTER TYPE)。不能从枚举类型中删除现有值,也不能更改此类值的排序顺序,除非删除并重新创建枚举类型。

您必须创建一个没有值的新类型,将旧类型的所有现有用法转换为使用新类型,然后删除旧类型。

例如

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
这太棒了!这样,我设法解决了Alembic迁移问题。由于(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
karantan,2016年

添加disable_ddl_transaction!迁移文件的顶部。
检查

从blah那里删除power ='god'; 在我的情况下
不起作用

1
TBH我不明白为什么选择这个答案。这个答案不正确!您可以使用指定的标签从pg_enum中删除值。
罗曼·波德利诺夫

2
@RomanPoelinov直接进行目录操作,后果自负。有一些原因使postgres不支持本地删除枚举值。与不受支持和不安全的目录黑客相比,这是“不正确的”吗?
Craig Ringer


32

如果要删除枚举类型的项目,则必须在PostgreSQL的系统表上进行操作。

使用此命令,可以显示所有项目的枚举类型。

SELECT * FROM pg_enum;

然后检查搜索的值是否唯一。为了在删除rekoru期间增加唯一性,除了“ enumlabel”之外,还必须传递“ enumtypid”。

此命令删除枚举类型的条目,其中“唯一”是您的值。

从pg_enum中删除en.enumtypid = 124 AND en.enumlabel ='unique';

注意 必须使用我描述的示例,因为我们偶然在枚举类型中添加了新值,但在数据库中的任何地方都没有使用过。


20
这是一个非常危险的操作,但是如果您知道自己在做什么,那么从枚举类型中删除一个值非常简洁明了。首先,确保没有表使用要删除的枚举值。如果你不这样做,你会严重打破引用枚举值中的所有表(例如,从这样的表选择将返回ERROR: invalid internal value for enum并产生NO的结果。)
克林特Pachl

5
没错,这是应该考虑的最重要方面。当我们偶然将新值添加到枚举类型中,但是我们没有在数据库中的任何地方使用它时,必须使用我描述的示例。
elcudro

1
鉴于此命令的危险性,DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';因此NOTE应该以粗体显示,而不是命令。如果您在某个表中使用了该值,则无法从中恢复。您无法更新包含该值的行,也无法转换。唯一的方法是删除整个行。
Sylvain

8

对于那些希望修改枚举值的人,重新创建它似乎是唯一可行且安全的解决方案。

它包括暂时将枚举列转换为字符串格式,重新创建枚举,然后将字符串列转换回枚举类型。

这是一个例子:

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;
Manuel Darveau

7

使用以下查询从Postgresql类型删除ENUM值

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中的typname是小写。因此,它不起作用,除非在SELECT oid FROM pg_type WHERE typname = 'enum_type'
fzerorubigd中

2

以编程方式执行此操作如下。可以使用与https://stackoverflow.com/a/47305844/629272中给出的常规步骤相同的通用步骤,但是对于我的目的而言,这些步骤比手动设置要合理得多(编写一个向下迁移的小写)。my_type,,my_type_oldvalue_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中有同样的问题。Postgres。删除需要某些程序,如果顺序不正确,则甚至有可能将表锁定以进行读取。

编写了一个方便的脚本来删除。已经证明了其性能的数倍。但是,此过程涉及将删除的值替换为新的值(如果表字段允许,则该值为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中删除一个确切值的代码吗?
Zaytsev Dmitry,

2
@ZaytsevDmitry您在这里:DELETE FROM pg_enum WHERE enumlabel='saml' AND enumsortorder=4;
Roman Podlinov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.