我有一个使用枚举类型的表列。我希望更新枚举类型有一个额外的可能值。我不想删除任何现有的值,只是添加新的值。最简单的方法是什么?


当前回答

免责声明:我没有尝试过这个解决方案,所以它可能不起作用;-)

您应该查看pg_enum。如果你只想改变一个现有ENUM的标签,一个简单的UPDATE就可以做到。

添加一个新的ENUM值。

首先将新值插入到pg_enum中。如果新值必须是最后一个,那么就完成了。 如果不是(你需要一个新的ENUM值在现有的值之间),你将不得不更新你的表中每个不同的值,从最高到最低… 然后你只需要在pg_enum中以相反的顺序重命名它们。

插图 您有以下一组标签:

ENUM ('enum1', 'enum2', 'enum3')

你想要得到:

ENUM ('enum1', 'enum1b', 'enum2', 'enum3')

然后:

INSERT INTO pg_enum (OID, 'newenum3');
UPDATE TABLE SET enumvalue TO 'newenum3' WHERE enumvalue='enum3';
UPDATE TABLE SET enumvalue TO 'enum3' WHERE enumvalue='enum2';

然后:

UPDATE TABLE pg_enum SET name='enum1b' WHERE name='enum2' AND enumtypid=OID;

等等……

其他回答

来自Postgres 9.1文档:

ALTER TYPE name ADD VALUE new_enum_value [ { BEFORE | AFTER } existing_enum_value ]

例子:

ALTER TYPE user_status ADD VALUE 'PROVISIONAL' AFTER 'NORMAL'
DB::statement("ALTER TABLE users DROP CONSTRAINT    users_user_type_check");
$types = ['old_type1', 'old_type1', 'new_type3'];
$result = join( ', ', array_map(function ($value){
           return sprintf("'%s'::character varying", $value);
       }, $types));
DB::statement("ALTER TABLE users ADD CONSTRAINT users_user_type_check CHECK (user_type::text = ANY    (ARRAY[$result]::text[]))");

最简单的方法:去掉枚举。它们不容易修改,因此应该很少使用。

不能添加注释到适当的位置,但ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type默认列失败。我不得不:

ALTER COLUMN bar DROP DEFAULT

然后就成功了。

下面是一个更通用但工作相当快的解决方案,除了更改类型本身之外,还使用它更新数据库中的所有列。即使ENUM的新版本不同于多个标签或遗漏了一些原始标签,也可以应用该方法。下面的代码替换my_schema。my_type ENUM (a, b, c)与枚举(“a”、“b”,“d”,“e”):

CREATE OR REPLACE FUNCTION tmp() RETURNS BOOLEAN AS
$BODY$

DECLARE
    item RECORD;

BEGIN

    -- 1. create new type in replacement to my_type
    CREATE TYPE my_schema.my_type_NEW
        AS ENUM ('a', 'b', 'd', 'e');

    -- 2. select all columns in the db that have type my_type
    FOR item IN
        SELECT table_schema, table_name, column_name, udt_schema, udt_name
            FROM information_schema.columns
            WHERE
                udt_schema   = 'my_schema'
            AND udt_name     = 'my_type'
    LOOP
        -- 3. Change the type of every column using my_type to my_type_NEW
        EXECUTE
            ' ALTER TABLE ' || item.table_schema || '.' || item.table_name
         || ' ALTER COLUMN ' || item.column_name
         || ' TYPE my_schema.my_type_NEW'
         || ' USING ' || item.column_name || '::text::my_schema.my_type_NEW;';
    END LOOP;

    -- 4. Delete an old version of the type
    DROP TYPE my_schema.my_type;

    -- 5. Remove _NEW suffix from the new type
    ALTER TYPE my_schema.my_type_NEW
        RENAME TO my_type;

    RETURN true;

END
$BODY$
LANGUAGE 'plpgsql';

SELECT * FROM tmp();
DROP FUNCTION tmp();

整个过程将运行得相当快,因为如果标签的顺序保持不变,则不会发生实际的数据更改。我使用my_type在5个表上应用了这个方法,每个表中有50,000 - 70,000行,整个过程只花了10秒。

当然,如果在数据的某个地方使用了新版本ENUM中缺少的标签,则该函数将返回一个异常,但在这种情况下,无论如何都应该事先做一些事情。