tipo postgres enum dato database postgresql enums

database - dato - PostgreSQL-Actualización de un tipo Enum



enum en postgresql (15)

Tengo un campo que usa un tipo de enumeración. Deseo actualizar la enumeración para tener un campo adicional (no quiero borrar nada, solo agregue una nueva etiqueta). ¿Cuál es la forma más simple de hacer esto?


Al usar Navicat puede ir a tipos (en vista -> otros -> tipos) - obtener la vista de diseño del tipo - y hacer clic en el botón "agregar etiqueta".


Aquí hay una solución más general pero bastante rápida, que además de cambiar el tipo en sí actualiza todas las columnas de la base de datos que la utilizan. El método se puede aplicar incluso si una nueva versión de ENUM es diferente en más de una etiqueta o si se pierden algunas de las originales. El código siguiente reemplaza my_schema.my_type AS ENUM (''a'', ''b'', ''c'') con 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();

Todo el proceso se ejecutará con bastante rapidez, porque si el orden de las etiquetas persiste, no ocurrirá ningún cambio de datos. my_type el método en 5 tablas usando my_type y teniendo 50,000-70,000 filas en cada una, y todo el proceso tomó solo 10 segundos.

Por supuesto, la función devolverá una excepción en caso de que las etiquetas que faltan en la nueva versión del ENUM se utilicen en alguna parte de los datos, pero en tal situación algo debe hacerse de antemano de todos modos.


Complementando @Dariusz 1

Para Rails 4.2.1, hay esta sección de documento:

== Migraciones transaccionales

Si el adaptador de base de datos admite transacciones DDL, todas las migraciones se envolverán automáticamente en una transacción. Sin embargo, hay consultas que no puede ejecutar dentro de una transacción, y para estas situaciones puede desactivar las transacciones automáticas.

class ChangeEnum < ActiveRecord::Migration disable_ddl_transaction! def up execute "ALTER TYPE model_size ADD VALUE ''new_value''" end end


De la documentación de Postgres 9.1:

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

Ejemplo:

ALTER TYPE user_status ADD VALUE ''PROVISIONAL'' AFTER ''NORMAL''


La actualización de pg_enum funciona, al igual que el truco de columna intermedia resaltado anteriormente. También se puede usar la función USING magic para cambiar el tipo de columna directamente:

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;

Mientras no tengas funciones que explícitamente requieran o devuelvan esa enumeración, estás bien. (pgsql se quejará cuando suelte el tipo si lo hay)

Además, tenga en cuenta que PG9.1 está introduciendo una instrucción ALTER TYPE, que funcionará en las enumeraciones:

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


Más simple: deshacerse de enums. No son fácilmente modificables y, por lo tanto, deberían usarse muy raramente.


No sé si tengo otra opción, pero podemos eliminar el valor usando:

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;


No se puede agregar un comentario al lugar apropiado, pero ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type con un valor predeterminado en la columna fallida. Tuve que:

ALTER table ALTER COLUMN bar DROP DEFAULT ;

y luego funcionó.


Para aquellos que buscan una solución dentro de la transacción, lo siguiente parece funcionar.

En lugar de un ENUM , se usará un DOMAIN en el tipo TEXT con una restricción que verifique que el valor se encuentre dentro de la lista especificada de valores permitidos (como lo sugieren algunos comentarios). El único problema es que no se puede agregar ninguna restricción (y, por lo tanto, tampoco modificar) a un dominio si es utilizado por cualquier tipo compuesto (los documentos simplemente dicen que "con el tiempo debería mejorarse"). Sin embargo, tal restricción puede solucionarse utilizando una restricción que invoca una función, de la siguiente manera.

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;

Previamente, utilicé una solución similar a la respuesta aceptada, pero está lejos de ser buena una vez que se consideran las vistas o las funciones o los tipos compuestos (y especialmente las vistas que usan otras vistas usando los ENUM modificados ...). La solución propuesta en esta respuesta parece funcionar bajo cualquier condición.

La única desventaja es que no se realizan comprobaciones en los datos existentes cuando se eliminan algunos valores permitidos (lo que podría ser aceptable, especialmente para esta pregunta). (Una llamada a ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check termina con el mismo error que agregar una nueva restricción al dominio utilizado por un tipo compuesto, desafortunadamente).

Tenga en cuenta que una pequeña modificación como CHECK (value = ANY(get_allowed_values())) , donde get_allowed_values() devolvió la lista de valores permitidos, no funcionaría, lo cual es bastante extraño, por lo que espero que la solución propuesta anteriormente funcione de manera confiable ( lo hace por mí, hasta ahora ...). (Funciona, en realidad, fue mi error)


Parece que no puedo publicar un comentario, así que solo diré que la actualización de pg_enum funciona en Postgres 8.4. Por la forma en que se configuran nuestras enumeraciones, agregué nuevos valores a los tipos de enumeración existentes a través de:

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

Da un poco de miedo, pero tiene sentido dado el modo en que Postgres almacena sus datos.


PostgreSQL 9.1 presenta la capacidad de ALTERAR tipos Enum:

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


Si cae en una situación en la que debe agregar valores enum en la transacción, fe ejecutarlo en migración de ruta en ALTER TYPE , obtendrá error ERROR: ALTER TYPE ... ADD cannot run inside a transaction block (consulte el número de migración # 350 ) puede agregar dichos valores en pg_enum directamente como solución alternativa ( type_egais_units es el nombre de la enum de destino):

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 )


Una posible solución es la siguiente; La condición previa es que no haya conflictos en los valores enum utilizados. (por ejemplo, al eliminar un valor enum, asegúrese de que este valor ya no se use).

-- rename the old enum alter type old_enum rename to old_enum__; -- create the new enum create type new_enum as enum (''value1'', ''value2'', ''value3''); -- alter all you enum columns alter table my_table alter column my_column type new_enum using my_column::text::new_enum; -- drop the old enum drop type old_enum__;

También de esta manera, el orden de las columnas no cambiará.


Descargo de responsabilidad: No he probado esta solución, por lo que podría no funcionar ;-)

Deberías mirar pg_enum . Si solo desea cambiar la etiqueta de un ENUM existente, una simple ACTUALIZACIÓN lo hará.

Para agregar nuevos valores ENUM:

  • Primero inserte el nuevo valor en pg_enum . Si el nuevo valor tiene que ser el último, ya está listo.
  • Si no (necesita un nuevo valor ENUM entre los existentes), tendrá que actualizar cada valor distinto en su tabla, yendo del más alto al más bajo ...
  • Entonces solo tendrá que cambiarles el nombre en pg_enum en el orden opuesto.

Ilustración
Usted tiene el siguiente conjunto de etiquetas:

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

y quieres obtener:

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

entonces:

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

entonces:

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

Y así...


NOTA si está utilizando PostgreSQL 9.1 o posterior, consulte esta respuesta para un enfoque más simple.

Tuve el mismo problema hace unos días y encontré esta publicación. Entonces mi respuesta puede ser útil para alguien que está buscando una solución :)

Si solo tiene una o dos columnas que usan el tipo de enumeración que desea cambiar, puede intentarlo. También puede cambiar el orden de los valores en el nuevo tipo.

-- 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 debe repetirse si hay más de 1 columna.