我如何在postgresql中删除我创建的枚举类型的值?
create type admin_level1 as enum('classifier', 'moderator', 'god');
例如,我想从列表中移除moderator
。
在文档中似乎找不到相关信息。
我正在使用Postgresql 9.3.4。
我如何在postgresql中删除我创建的枚举类型的值?
create type admin_level1 as enum('classifier', 'moderator', 'god');
例如,我想从列表中移除moderator
。
在文档中似乎找不到相关信息。
我正在使用Postgresql 9.3.4。
DROP TYPE
命令删除枚举类型:DROP TYPE admin_level1;
你是否想知道如何从枚举类型中删除单个值?如果是这样,那么很遗憾,无法实现。因为这不被支持:(请参见):
虽然枚举类型主要用于静态值集,但支持向现有枚举类型添加新值以及重命名值(请参见
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;
这里写得非常好:
http://blog.yo1.dog/updating-enum-values-in-postgresql-the-safe-and-easy-way/
重命名现有类型
ALTER TYPE status_enum RENAME TO status_enum_old;
创建新类型
CREATE TYPE status_enum AS ENUM('queued', 'running', 'done');
更新列以使用新类型
ALTER TABLE job ALTER COLUMN job_status TYPE status_enum USING job_status::text::status_enum;
移除旧类型
DROP TYPE status_enum_old;
可能出现的错误和故障排除:
invalid input value for enum {enum name}: "{some value}"
- 一个或多个行的值 ("{some value}"
) 不在新类型中。 在更新列类型之前,您必须处理这些行。default for column "{column_name}" cannot be cast automatically to type {enum_name}
- 列的默认值不在新类型中。 您必须在更新列类型之前更改或删除列的默认值。 感谢 Philipp 提供的内容。cannot alter type of a column used by a view or rule
- 所有使用该列的视图和规则必须在执行 ALTER 命令之前被删除,然后在之后重新创建。 有自动执行此操作的方法。ERROR 25001: ALTER TYPE ... ADD
不能在事务块内运行。 - estrar<>
上遇到 operator does not exist
错误。我不得不放弃使用该列的检查约束条件,修改该字段,并重新添加约束条件。 - edA-qa mort-ora-ySELECT * FROM pg_enum;
DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unique';
DELETE FROM pg_enum en WHERE en.enumtypid=124 AND en.enumlabel='unigue';
应该加粗提示注意事项,而不是加粗命令。如果您在某个表中使用了该值,将无法从中恢复。您不能更新包含该值的行,也不能进行转换。唯一的方法是删除整行。 - Sylvain对于那些希望修改枚举值的人来说,重新创建枚举似乎是唯一可行且安全的解决方案。
这个方法包括将枚举列暂时转换为字符串格式,重新创建枚举,然后再将字符串列转换回枚举类型。
以下是一个例子:
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;
should be 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 Darveauselect * from pg_enum where enumlabel='Manager';update pg_enum set enumlabel = 'FacilityManager' where oid=489188;
- gerardw使用以下查询从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**';
删除之前,将类型值更新为新的类型值或现有值。
以下是以程序化方式实现的方法。与https://dev59.com/YF8e5IYBdhLWcg3wYJiu#47305844中给出的相同的总体步骤是适当的,但对于我的目的(编写alembic向下迁移)来说,那些步骤过于繁琐。当然,my_type
,my_type_old
和value_to_delete
应相应更改。
Rename your type.
ALTER TYPE my_type RENAME TO my_type_old;
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 $$;
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 $$;
Delete the old type.
DROP TYPE my_type_old;
无法从ENUM中删除单个值,唯一的解决方案是使用所需的值删除并重新创建ENUM。
DELETE FROM pg_enum
,因为我遇到了权限错误。-- https://dev59.com/YF8e5IYBdhLWcg3wYJiu#62444685
-- https://dev59.com/YF8e5IYBdhLWcg3wYJiu#51073579
create or replace function remove_enum_value(
type_name text, -- Name of the type where you need to remove a value from
value_to_delete text, -- Specific value of the given type you want to remove
value_fallback text, -- Which new value columns will have instead of the value deleted
column_default text -- DEFAULT value for the column after type alteration (DEFAULT need to be disabled before changing type, https://dev59.com/P1gR5IYBdhLWcg3wlOEr#41149789)
)
RETURNS VOID AS $body$
declare
-- Used as temporary type
_type_name_tmp text := type_name || '_tmp_' || floor(extract(epoch from now()) * 1000);
-- Used to store statements to execute
_sql text;
-- Used to loop tables and switch type from current to temporary
_column_data record;
_table_name varchar(255);
_column_name varchar(255);
begin
--------------------------------------------------------------------------------------------------------------
-- Check: required inputs
if type_name is null
then
raise exception 'Parameter type_name is null';
end if;
if value_to_delete is null
then
raise exception 'Parameter value_to_delete is null';
end if;
-- Check: type exists
IF not EXISTS (SELECT 1 FROM pg_type WHERE typname = type_name) THEN
raise info 'Type %s does not exists', type_name;
return;
END IF;
-- Check: value to delete exists
if not exists(
select *
FROM pg_enum -- check existing of value to delete
WHERE enumtypid = (select oid from pg_type where typName=cast(type_name as varchar) limit 1) and enumlabel=cast(value_to_delete as varchar)
)
then
raise info 'Value to delete % does not exists in type %s', value_to_delete, type_name;
return;
end if;
-- Check: fallback value is provided and exists
if value_fallback is not null and not exists(
select *
FROM pg_enum -- check existing of value to delete
where
enumtypid = (select oid from pg_type where typName=cast(type_name as varchar) limit 1)
and enumlabel=cast(value_fallback as varchar)
)
then
raise info 'Fallback value % does not exists in type %s', value_fallback, type_name;
return;
end if;
-- Check values are different
if value_fallback = value_to_delete
then
raise info 'Value to delete %s is the same as fallback value %', value_to_delete, value_fallback;
return;
end if;
raise info 'Checks passed, ready to process!';
--------------------------------------------------------------------------------------------------------------
-- Retrieve current values of type
_sql := format('
SELECT string_agg(quote_literal(value), '','')
FROM unnest(enum_range(NULL::%s)) value
WHERE value <> ''%s''
', type_name, value_to_delete);
raise info '%', _sql;
execute _sql into _sql;
-- Create temporary enum
_sql := format(
'CREATE TYPE %s AS ENUM (%s)',
_type_name_tmp,
_sql
);
raise info '%', _sql;
execute _sql;
-- Rename all values from value that need to be deleted to new value (selecting all tables with schemas which has column with enum relation)
for _column_data in (
select
concat(c.table_schema,'.',c.table_name ) as table_name,
c.column_name
FROM information_schema.columns c
where
c.udt_name = cast(type_name as varchar)
and c.table_schema=c.udt_schema
and data_type = 'USER-DEFINED'
)
LOOP
_sql:= format('UPDATE %1$s set %2$s = %3$L where %2$s=%4$L', _column_data.table_name, _column_data.column_name, value_fallback, value_to_delete);
raise info 'Update by looping: %', _sql;
EXECUTE _sql;
END LOOP;
-- Switch type from current to temporary
FOR _column_data in (
SELECT cols.table_name, cols.column_name
FROM information_schema.columns cols
WHERE udt_name = type_name
)
LOOP
_table_name := _column_data.table_name;
_column_name := _column_data.column_name;
_sql := format(
'
ALTER TABLE %s
ALTER COLUMN %s DROP DEFAULT,
ALTER COLUMN %s TYPE %s USING %s::text::%s,
ALTER COLUMN %s SET DEFAULT %s;
',
_table_name,
_column_name,
_column_name, _type_name_tmp, _column_name, _type_name_tmp,
_column_name, (case when column_default is null then null else '''' || column_default || '''::' || _type_name_tmp end)
);
raise info '%', _sql;
execute _sql;
END LOOP;
-- Drop previous type
_sql := format('DROP TYPE %s;', type_name);
raise info '%', _sql;
execute _sql;
-- Rename type to previous name
_sql := format('ALTER TYPE %s RENAME TO %s;', _type_name_tmp, type_name);
raise info '%', _sql;
execute _sql;
END $body$
LANGUAGE plpgsql;
在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 $$;
--column-inserts
导出,然后用文本编辑器编辑导出的文件,删除值后重新导入该文件。
(psycopg2.InternalError) ALTER TYPE ... ADD cannot run inside a transaction block
的错误,我无法添加新的枚举类型。 - karantan