NOTA: si está utilizando PostgreSQL 9.1 o posterior, y está de acuerdo con hacer cambios fuera de una transacción, vea 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.
ALTER TYPE
. Pero incluso antes de eso,ALTER TABLE foo ALTER COLUMN bar TYPE new_type USING bar::text::new_type;
era muy superior.ALTER TABLE some_table ALTER COLUMN some_column TYPE some_enum_type USING some_column::text::some_enum_type;
PostgreSQL 9.1 introduce la capacidad de ALTERAR tipos de enumeración:
fuente
Una posible solución es la siguiente; La condición previa es que no hay conflictos en los valores de enumeración utilizados. (por ejemplo, al eliminar un valor de enumeración, asegúrese de que este valor ya no se use).
También de esta manera no se cambiará el orden de las columnas.
fuente
pg_enum
que realmente pueden romper cosas y es transaccional, a diferenciaALTER TYPE ... ADD
.default for column "my_column" cannot be cast automatically to type "my_enum"
. Deberá hacer lo siguiente:ALTER TABLE "my_table" ALTER COLUMN "my_column" DROP DEFAULT, ALTER COLUMN "my_column" TYPE "my_type" USING ("my_column"::text::"my_type"), ALTER COLUMN "my_column" SET DEFAULT 'my_default_value';
Si se encuentra en una situación en la que debe agregar
enum
valores en la transacción, por ejemplo, ejecútelo en la migración de ruta de acceso en laALTER TYPE
instrucción, recibirá un errorERROR: ALTER TYPE ... ADD cannot run inside a transaction block
(vea el problema de ruta # 350 ) en el que podría agregar dichos valorespg_enum
directamente como solución alternativa (type_egais_units
es el nombre del objetivoenum
):fuente
Complementando a @Dariusz 1
Para Rails 4.2.1, existe esta sección de documentación:
== Migraciones transaccionales
Si el adaptador de base de datos admite transacciones DDL, todas las migraciones se incluirá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.
fuente
De la documentación de Postgres 9.1 :
Ejemplo:
fuente
Descargo de responsabilidad: no he probado esta solución, por lo que podría no funcionar ;-)
Deberías estar mirando
pg_enum
. Si solo desea cambiar la etiqueta de un ENUM existente, una simple ACTUALIZACIÓN lo hará.Para agregar un nuevo valor ENUM:
pg_enum
. Si el nuevo valor tiene que ser el último, ya está.pg_enum
en el orden opuesto.Ilustración
Tiene el siguiente conjunto de etiquetas:
y quieres obtener:
luego:
luego:
Y así...
fuente
Parece que no puedo publicar un comentario, así que solo diré que actualizar pg_enum funciona en Postgres 8.4. Por la forma en que están configuradas nuestras enumeraciones, he agregado nuevos valores a los tipos de enumeración existentes a través de:
Da un poco de miedo, pero tiene sentido dada la forma en que Postgres realmente almacena sus datos.
fuente
La actualización de pg_enum funciona, al igual que el truco de la columna intermedia resaltado anteriormente. También se puede usar USING magic para cambiar el tipo de columna directamente:
Mientras no tenga funciones que requieran o devuelvan explícitamente esa enumeración, está bien. (pgsql se quejará cuando suelte el tipo si lo hay).
Además, tenga en cuenta que PG9.1 está introduciendo una declaración ALTER TYPE, que funcionará en enumeraciones:
http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html
fuente
ALTER TABLE foo ALTER COLUMN bar TYPE test USING bar::text::new_type;
Pero en gran medida irrelevante ahora ...... USING bar::type
funcionó para mí. Ni siquiera tuve que especificar::text
.Más simple: deshacerse de las enumeraciones. Ellos no son fácilmente modificables, y por lo tanto deben muy rara vez se pueden usar.
fuente
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 falló. Tuve que:ALTER table ALTER COLUMN bar DROP DEFAULT
;Y luego funcionó.
fuente
por si acaso, si está utilizando Rails y tiene varias declaraciones, deberá ejecutar una por una, como:
fuente
Aquí hay una solución más general pero de trabajo bastante rápido, que además de cambiar el tipo en sí, actualiza todas las columnas de la base de datos que la usan. El método puede aplicarse incluso si una nueva versión de ENUM es diferente en más de una etiqueta o pierde algunas de las originales. El siguiente código reemplaza
my_schema.my_type AS ENUM ('a', 'b', 'c')
conENUM ('a', 'b', 'd', 'e')
:Todo el proceso se ejecutará con bastante rapidez, porque si el orden de las etiquetas persiste, no ocurrirá ningún cambio real de datos. Apliqué 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 de ENUM se usen en algún lugar de los datos, pero en tal situación se debe hacer algo de antemano de todos modos.
fuente
Para aquellos que buscan una solución en la transacción, lo siguiente parece funcionar.
En lugar de un
ENUM
,DOMAIN
se utilizará un tipoTEXT
con una restricción que verifica que el valor esté dentro de la lista especificada de valores permitidos (como sugieren algunos comentarios). El único problema es que no se puede agregar ninguna restricción (y, por lo tanto, tampoco modificarla) a un dominio si es utilizada por cualquier tipo compuesto (los documentos simplemente dicen que "eventualmente debería mejorarse"). Sin embargo, tal restricción se puede solucionar utilizando una restricción que llame a una función, como se indica a continuación.Anteriormente, utilicé una solución similar a la respuesta aceptada, pero está lejos de ser buena una vez que se consideran las vistas o funciones o tipos compuestos (y especialmente las vistas que usan otras vistas que usan los ENUM modificados ...). La solución propuesta en esta respuesta parece funcionar bajo cualquier condición.
La única desventaja es que no se realizan verificaciones 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 ligera modificación como(funciona, de hecho, fue mi error)CHECK (value = ANY(get_allowed_values()))
, por ejemplo , donde laget_allowed_values()
función 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 para mí, hasta ahora ...).fuente
Como se discutió anteriormente, el
ALTER
comando no se puede escribir dentro de una transacción. La forma sugerida es insertar directamente en la tabla pg_enum, porretrieving the typelem from pg_type table
ycalculating the next enumsortorder number
;El siguiente es el código que uso. (Comprueba si existe un valor duplicado antes de insertar (restricción entre enumtypid y enumlabel name)
Tenga en cuenta que su nombre de tipo se antepone con un guión bajo en la tabla pg_type. Además, el nombre típico debe estar en minúsculas en la cláusula where.
Ahora esto se puede escribir de forma segura en su secuencia de comandos db migrate.
fuente
No sé si tiene otra opción, pero podemos soltar el valor usando:
fuente
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".
fuente
ERROR: cannot drop type foo because other objects depend on it HINT: Use DROP ... CASCADE to drop the dependent objects too.