向现有 ENUM 类型添加新值

发布于 2024-08-11 10:06:16 字数 103 浏览 2 评论 0原文

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

I have a table column that uses an enum type. I wish to update that enum type to have an additional possible value. I don't want to delete any existing values, just add the new value. What is the simplest way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

南…巷孤猫 2024-08-18 10:06:16

PostgreSQL 9.1 引入了 ALTER 枚举类型的功能:

ALTER TYPE enum_type ADD VALUE 'new_value'; -- appends to list
ALTER TYPE enum_type ADD VALUE 'new_value' BEFORE 'old_value';
ALTER TYPE enum_type ADD VALUE 'new_value' AFTER 'old_value';

PostgreSQL 9.1 introduces ability to ALTER Enum types:

ALTER TYPE enum_type ADD VALUE 'new_value'; -- appends to list
ALTER TYPE enum_type ADD VALUE 'new_value' BEFORE 'old_value';
ALTER TYPE enum_type ADD VALUE 'new_value' AFTER 'old_value';
静赏你的温柔 2024-08-18 10:06:16

注意如果您使用的是 PostgreSQL 9.1 或更高版本,并且可以在事务之外进行更改,请参阅 这个答案以获得更简单的方法。


几天前我也遇到了同样的问题,并找到了这篇文章。因此,我的答案对于正在寻找解决方案的人可能会有所帮助:)

如果您只有一两列使用您想要更改的枚举类型,您可以尝试此操作。您还可以更改新类型中值的顺序。

-- 1. rename the enum type you want to change
alter type some_enum_type rename to _some_enum_type;
-- 2. create new type
create type some_enum_type as enum ('old', 'values', 'and', 'new', 'ones');
-- 3. rename column(s) which uses our enum type
alter table some_table rename column some_column to _some_column;
-- 4. add new column of new type
alter table some_table add some_column some_enum_type not null default 'new';
-- 5. copy values to the new column
update some_table set some_column = _some_column::text::some_enum_type;
-- 6. remove old column and type
alter table some_table drop column _some_column;
drop type _some_enum_type;

如果多于 1 列,则应重复 3-6。

NOTE if you're using PostgreSQL 9.1 or later, and you are ok with making changes outside of a transaction, see this answer for a simpler approach.


I had the same problem few days ago and found this post. So my answer can be helpful for someone who is looking for solution :)

If you have only one or two columns which use the enum type you want to change, you can try this. Also you can change the order of values in the new type.

-- 1. rename the enum type you want to change
alter type some_enum_type rename to _some_enum_type;
-- 2. create new type
create type some_enum_type as enum ('old', 'values', 'and', 'new', 'ones');
-- 3. rename column(s) which uses our enum type
alter table some_table rename column some_column to _some_column;
-- 4. add new column of new type
alter table some_table add some_column some_enum_type not null default 'new';
-- 5. copy values to the new column
update some_table set some_column = _some_column::text::some_enum_type;
-- 6. remove old column and type
alter table some_table drop column _some_column;
drop type _some_enum_type;

3-6 should be repeated if there is more than 1 column.

恍梦境° 2024-08-18 10:06:16

一个可能的解决方案如下:前提是所使用的枚举值不存在冲突。 (例如,当删除枚举值时,请确保不再使用该值。)

-- rename the old enum
alter type my_enum rename to my_enum__;
-- create the new enum
create type my_enum as enum ('value1', 'value2', 'value3');

-- alter all you enum columns
alter table my_table
  alter column my_column type my_enum using my_column::text::my_enum;

-- drop the old enum
drop type my_enum__;

同样,通过这种方式,列顺序也不会改变。

A possible solution is the following; precondition is, that there are not conflicts in the used enum values. (e.g. when removing an enum value, be sure that this value is not used anymore.)

-- rename the old enum
alter type my_enum rename to my_enum__;
-- create the new enum
create type my_enum as enum ('value1', 'value2', 'value3');

-- alter all you enum columns
alter table my_table
  alter column my_column type my_enum using my_column::text::my_enum;

-- drop the old enum
drop type my_enum__;

Also in this way the column order will not be changed.

不喜欢何必死缠烂打 2024-08-18 10:06:16

如果您使用的是 Postgres 12(或更高版本),您只需在事务内运行 ALTER TYPE ... ADD VALUE (文档)。

如果 ALTER TYPE ... ADD VALUE(向枚举添加新值的形式
type) 在事务块内执行,新值不能被执行
直到事务提交后才使用。

所以迁移中不需要黑客。

UPD:这是一个示例(感谢 Nick)

ALTER TYPE enum_type ADD VALUE 'new_value';

If you are using Postgres 12 (or later) you can just run ALTER TYPE ... ADD VALUE inside of transaction (documentation).

If ALTER TYPE ... ADD VALUE (the form that adds a new value to an enum
type) is executed inside a transaction block, the new value cannot be
used until after the transaction has been committed.

So no hacks needed in migrations.

UPD: here is an example (thanks to Nick for it)

ALTER TYPE enum_type ADD VALUE 'new_value';

半暖夏伤 2024-08-18 10:06:16

如果您遇到应在事务中添加 enum 值的情况,并在 Flyway 迁移中的 ALTER TYPE 语句上执行它,您将收到错误 ERROR: ALTER TYPE ... ADD 无法在事务块内运行(请参阅 flyway 问题 #350) 您可以将这些值直接添加到 pg_enum 中作为解决方法(type_egais_units 是目标 enum 的名称):

INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
    SELECT 'type_egais_units'::regtype::oid, 'NEW_ENUM_VALUE', ( SELECT MAX(enumsortorder) + 1 FROM pg_enum WHERE enumtypid = 'type_egais_units'::regtype )

If you fall into situation when you should add enum values in transaction, f.e. execute it in flyway migration on ALTER TYPE statement you will be get error ERROR: ALTER TYPE ... ADD cannot run inside a transaction block (see flyway issue #350) you could add such values into pg_enum directly as workaround (type_egais_units is name of target enum):

INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
    SELECT 'type_egais_units'::regtype::oid, 'NEW_ENUM_VALUE', ( SELECT MAX(enumsortorder) + 1 FROM pg_enum WHERE enumtypid = 'type_egais_units'::regtype )
梦幻的心爱 2024-08-18 10:06:16

补充@Dariusz 1

对于Rails 4.2.1,有这个文档部分:

== 事务性迁移

如果数据库适配器支持 DDL 事务,则所有迁移都将
自动包含在事务中。有疑问您
但不能在事务内执行,对于这些情况
您可以关闭自动交易。

class ChangeEnum < ActiveRecord::Migration
  disable_ddl_transaction!

  def up
    execute "ALTER TYPE model_size ADD VALUE 'new_value'"
  end
end

Complementing @Dariusz 1

For Rails 4.2.1, there's this doc section:

== Transactional Migrations

If the database adapter supports DDL transactions, all migrations will
automatically be wrapped in a transaction. There are queries that you
can't execute inside a transaction though, and for these situations
you can turn the automatic transactions off.

class ChangeEnum < ActiveRecord::Migration
  disable_ddl_transaction!

  def up
    execute "ALTER TYPE model_size ADD VALUE 'new_value'"
  end
end
分开我的手 2024-08-18 10:06:16

以防万一,如果您使用 Rails 并且有多个语句,则需要一一执行,例如:

execute "ALTER TYPE XXX ADD VALUE IF NOT EXISTS 'YYY';"
execute "ALTER TYPE XXX ADD VALUE IF NOT EXISTS 'ZZZ';"

just in case, if you are using Rails and you have several statements you will need to execute one by one, like:

execute "ALTER TYPE XXX ADD VALUE IF NOT EXISTS 'YYY';"
execute "ALTER TYPE XXX ADD VALUE IF NOT EXISTS 'ZZZ';"
挽清梦 2024-08-18 10:06:16

从 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'

From Postgres 9.1 Documentation:

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

Example:

ALTER TYPE user_status ADD VALUE 'PROVISIONAL' AFTER 'NORMAL'
风透绣罗衣 2024-08-18 10:06:16

免责声明:我还没有尝试过此解决方案,因此它可能不起作用;-)

您应该查看pg_enum。如果您只想更改现有 ENUM 的标签,只需简单的 UPDATE 即可完成。

要添加新的 ENUM 值:

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

插图
您有以下一组标签:

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

并且您想要获取:

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

then:

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

then:

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

等等...

Disclaimer: I haven't tried this solution, so it might not work ;-)

You should be looking at pg_enum. If you only want to change the label of an existing ENUM, a simple UPDATE will do it.

To add a new ENUM values:

  • First insert the new value into pg_enum. If the new value has to be the last, you're done.
  • If not (you need to a new ENUM value in between existing ones), you'll have to update each distinct value in your table, going from the uppermost to the lowest...
  • Then you'll just have to rename them in pg_enum in the opposite order.

Illustration
You have the following set of labels:

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

and you want to obtain:

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

then:

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

then:

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

And so on...

倾听心声的旋律 2024-08-18 10:06:16

我似乎无法发表评论,所以我只想说更新 pg_enum 在 Postgres 8.4 中有效。对于我们的枚举设置方式,我通过以下方式向现有枚举类型添加了新值:

INSERT INTO pg_enum (enumtypid, enumlabel)
  SELECT typelem, 'NEWENUM' FROM pg_type WHERE
    typname = '_ENUMNAME_WITH_LEADING_UNDERSCORE';

这有点可怕,但考虑到 Postgres 实际存储其数据的方式,这是有道理的。

I can't seem to post a comment, so I'll just say that updating pg_enum works in Postgres 8.4 . For the way our enums are set up, I've added new values to existing enum types via:

INSERT INTO pg_enum (enumtypid, enumlabel)
  SELECT typelem, 'NEWENUM' FROM pg_type WHERE
    typname = '_ENUMNAME_WITH_LEADING_UNDERSCORE';

It's a little scary, but it makes sense given the way Postgres actually stores its data.

请持续率性 2024-08-18 10:06:16

更新 pg_enum 是有效的,就像上面突出显示的中间列技巧一样。人们还可以使用 USING magic 直接更改列的类型:

CREATE TYPE test AS enum('a', 'b');
CREATE TABLE foo (bar test);
INSERT INTO foo VALUES ('a'), ('b');

ALTER TABLE foo ALTER COLUMN bar TYPE varchar;

DROP TYPE test;
CREATE TYPE test as enum('a', 'b', 'c');

ALTER TABLE foo ALTER COLUMN bar TYPE test
USING CASE
WHEN bar = ANY (enum_range(null::test)::varchar[])
THEN bar::test
WHEN bar = ANY ('{convert, these, values}'::varchar[])
THEN 'c'::test
ELSE NULL
END;

只要您没有明确要求或返回该枚举的函数,就可以了。 (如果有的话,当你删除类型时,pgsql 会抱怨。)

另外,请注意 PG9.1 引入了 ALTER TYPE 语句,该语句适用于枚举:

http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html

Updating pg_enum works, as does the intermediary column trick highlighted above. One can also use USING magic to change the column's type directly:

CREATE TYPE test AS enum('a', 'b');
CREATE TABLE foo (bar test);
INSERT INTO foo VALUES ('a'), ('b');

ALTER TABLE foo ALTER COLUMN bar TYPE varchar;

DROP TYPE test;
CREATE TYPE test as enum('a', 'b', 'c');

ALTER TABLE foo ALTER COLUMN bar TYPE test
USING CASE
WHEN bar = ANY (enum_range(null::test)::varchar[])
THEN bar::test
WHEN bar = ANY ('{convert, these, values}'::varchar[])
THEN 'c'::test
ELSE NULL
END;

As long as you've no functions that explicitly require or return that enum, you're good. (pgsql will complain when you drop the type if there are.)

Also, note that PG9.1 is introducing an ALTER TYPE statement, which will work on enums:

http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html

苹果你个爱泡泡 2024-08-18 10:06:16

无法将注释添加到适当的位置,但使用列默认值的 ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type 失败。我必须:

ALTER table ALTER COLUMN bar DROP DEFAULT;

然后就成功了。

Can't add a comment to the appropriate place, but ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type with a default on the column failed. I had to:

ALTER table ALTER COLUMN bar DROP DEFAULT;

and then it worked.

卸妝后依然美 2024-08-18 10:06:16

这是一个更通用但运行速度相当快的解决方案,除了更改类型本身之外,它还使用它更新数据库中的所有列。即使新版本的 ENUM 与多个标签不同或遗漏了一些原始标签,也可以应用该方法。下面的代码将 my_schema.my_type AS ENUM ('a', 'b', 'c') 替换为 ENUM ('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 中缺少的标签,该函数将返回异常,但在这种情况下无论如何都应该事先完成一些操作。

Here is a more general but a rather fast-working solution, which apart from changing the type itself updates all columns in the database using it. The method can be applied even if a new version of ENUM is different by more than one label or misses some of the original ones. The code below replaces my_schema.my_type AS ENUM ('a', 'b', 'c') with ENUM ('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();

The whole process will run fairly quickly, because if the order of labels persists, no actual change of data will happen. I applied the method on 5 tables using my_type and having 50,000−70,000 rows in each, and the whole process took just 10 seconds.

Of course, the function will return an exception in case if labels that are missing in the new version of the ENUM are used somewhere in the data, but in such situation something should be done beforehand anyway.

爱你是孤单的心事 2024-08-18 10:06:16

对于那些寻找交易内解决方案的人来说,以下方法似乎有效。

应在类型 TEXT 上使用 DOMAIN,而不是 ENUM,并带有检查该值是否在指定的允许值列表内的约束(正如一些评论所建议的那样)。唯一的问题是,如果域被任何复合类型使用,则不能向域添加任何约束(因此也不能修改)(文档只是说“最终应该改进”)。然而,可以通过使用调用函数的约束来解决这种限制,如下所示。

START TRANSACTION;

CREATE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;

CREATE DOMAIN test_domain AS TEXT CONSTRAINT val_check CHECK (test_is_allowed_label(value));

CREATE TYPE test_composite AS (num INT, word test_domain);

CREATE TABLE test_table (val test_composite);
INSERT INTO test_table (val) VALUES ((1, 'one')::test_composite), ((3, 'three')::test_composite);
-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint

CREATE VIEW test_view AS SELECT * FROM test_table; -- just to show that the views using the type work as expected

CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three', 'four');
$function$ LANGUAGE SQL IMMUTABLE;

INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- allowed by the new effective definition of the constraint

SELECT * FROM test_view;

CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;

-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint, again

SELECT * FROM test_view; -- note the view lists the restricted value 'four' as no checks are made on existing data

DROP VIEW test_view;
DROP TABLE test_table;
DROP TYPE test_composite;
DROP DOMAIN test_domain;
DROP FUNCTION test_is_allowed_label(TEXT);

COMMIT;

以前,我使用了与已接受的答案类似的解决方案,但一旦考虑视图、函数或复合类型(尤其是使用修改后的 ENUM 的其他视图的视图...),它就远远不够好。这个答案中提出的解决方案似乎在任何条件下都有效。

唯一的缺点是,当删除某些允许的值时,不会对现有数据执行检查(这可能是可以接受的,特别是对于这个问题)。 (不幸的是,调用ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check最终会出现与向复合类型使用的域添加新约束相同的错误。)

请注意,稍作修改,例如CHECK (value = ANY(get_allowed_values())),其中 get_allowed_values() 函数返回允许值的列表,将不起作用 - 这很奇怪,所以我希望上面提出的解决方案可靠地工作(到目前为止,它对我来说是有效的......)。(实际上它有效 - 这是我的错误)

For those looking for an in-transaction solution, the following seems to work.

Instead of an ENUM, a DOMAIN shall be used on type TEXT with a constraint checking that the value is within the specified list of allowed values (as suggested by some comments). The only problem is that no constraint can be added (and thus neither modified) to a domain if it is used by any composite type (the docs merely says this "should eventually be improved"). Such a restriction may be worked around, however, using a constraint calling a function, as follows.

START TRANSACTION;

CREATE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;

CREATE DOMAIN test_domain AS TEXT CONSTRAINT val_check CHECK (test_is_allowed_label(value));

CREATE TYPE test_composite AS (num INT, word test_domain);

CREATE TABLE test_table (val test_composite);
INSERT INTO test_table (val) VALUES ((1, 'one')::test_composite), ((3, 'three')::test_composite);
-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint

CREATE VIEW test_view AS SELECT * FROM test_table; -- just to show that the views using the type work as expected

CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three', 'four');
$function$ LANGUAGE SQL IMMUTABLE;

INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- allowed by the new effective definition of the constraint

SELECT * FROM test_view;

CREATE OR REPLACE FUNCTION test_is_allowed_label(lbl TEXT) RETURNS BOOL AS $function$
    SELECT lbl IN ('one', 'two', 'three');
$function$ LANGUAGE SQL IMMUTABLE;

-- INSERT INTO test_table (val) VALUES ((4, 'four')::test_composite); -- restricted by the CHECK constraint, again

SELECT * FROM test_view; -- note the view lists the restricted value 'four' as no checks are made on existing data

DROP VIEW test_view;
DROP TABLE test_table;
DROP TYPE test_composite;
DROP DOMAIN test_domain;
DROP FUNCTION test_is_allowed_label(TEXT);

COMMIT;

Previously, I used a solution similar to the accepted answer, but it is far from being good once views or functions or composite types (and especially views using other views using the modified ENUMs...) are considered. The solution proposed in this answer seems to work under any conditions.

The only disadvantage is that no checks are performed on existing data when some allowed values are removed (which might be acceptable, especially for this question). (A call to ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check ends up with the same error as adding a new constraint to the domain used by a composite type, unfortunately.)

Note that a slight modification such as CHECK (value = ANY(get_allowed_values())), where get_allowed_values() function returned the list of allowed values, would not work - which is quite strange, so I hope the solution proposed above works reliably (it does for me, so far...). (it works, actually - it was my error)

星光不落少年眉 2024-08-18 10:06:16

如上所述,ALTER 命令不能写入事务内。建议的方法是直接插入到 pg_enum 表中,通过从 pg_type 表中检索 typelem计算下一个 enumsortorder 编号

以下是我使用的代码。 (在插入之前检查是否存在重复值(enumtypid 和 enumlabel 名称之间的约束)

INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
    SELECT typelem,
    'NEW_ENUM_VALUE',
    (SELECT MAX(enumsortorder) + 1 
        FROM pg_enum e
        JOIN pg_type p
        ON p.typelem = e.enumtypid
        WHERE p.typname = '_mytypename'
    )
    FROM pg_type p
    WHERE p.typname = '_mytypename'
    AND NOT EXISTS (
        SELECT * FROM 
        pg_enum e
        JOIN pg_type p
        ON p.typelem = e.enumtypid
        WHERE e.enumlabel = 'NEW_ENUM_VALUE'
        AND p.typname = '_mytypename'
    )

请注意,您的类型名称在 pg_type 表中前面带有下划线。此外,where 子句中的类型名称需要全部小写。

现在可以这样写安全地进入您的数据库迁移脚本。

As discussed above, ALTER command cannot be written inside a transaction. The suggested way is to insert into the pg_enum table directly, by retrieving the typelem from pg_type table and calculating the next enumsortorder number;

Following is the code that I use. (Checks if duplicate value exists before inserting (constraint between enumtypid and enumlabel name)

INSERT INTO pg_enum (enumtypid, enumlabel, enumsortorder)
    SELECT typelem,
    'NEW_ENUM_VALUE',
    (SELECT MAX(enumsortorder) + 1 
        FROM pg_enum e
        JOIN pg_type p
        ON p.typelem = e.enumtypid
        WHERE p.typname = '_mytypename'
    )
    FROM pg_type p
    WHERE p.typname = '_mytypename'
    AND NOT EXISTS (
        SELECT * FROM 
        pg_enum e
        JOIN pg_type p
        ON p.typelem = e.enumtypid
        WHERE e.enumlabel = 'NEW_ENUM_VALUE'
        AND p.typname = '_mytypename'
    )

Note that your type name is prepended with an underscore in the pg_type table. Also, the typname needs to be all lowercase in the where clause.

Now this can be written safely into your db migrate script.

如果没有你 2024-08-18 10:06:16
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[]))");
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[]))");
二智少女 2024-08-18 10:06:16

使用 Navicat 时,您可以转到类型(在视图 -> 其他 -> 类型下) - 获取类型的设计视图 - 然后单击“添加标签”按钮。

When using Navicat you can go to types (under view -> others -> types) - get the design view of the type - and click the "add label" button.

姐不稀罕 2024-08-18 10:06:16

我不知道是否还有其他选择,但我们可以使用以下方法删除该值:

select oid from pg_type where typname = 'fase';'
select * from pg_enum where enumtypid = 24773;'
select * from pg_enum where enumtypid = 24773 and enumsortorder = 6;
delete from pg_enum where enumtypid = 24773 and enumsortorder = 6;

I don't know if have other option but we can drop the value using:

select oid from pg_type where typname = 'fase';'
select * from pg_enum where enumtypid = 24773;'
select * from pg_enum where enumtypid = 24773 and enumsortorder = 6;
delete from pg_enum where enumtypid = 24773 and enumsortorder = 6;
清引 2024-08-18 10:06:16

最简单:摆脱枚举。它们不容易修改,因此应该非常很少使用。

Simplest: get rid of enums. They are not easily modifiable, and thus should very rarely be used.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文