REMARQUE si vous utilisez PostgreSQL 9.1 ou une version ultérieure et que vous êtes d'accord pour effectuer des modifications en dehors d'une transaction, consultez cette réponse pour une approche plus simple.
J'ai eu le même problème il y a quelques jours et j'ai trouvé ce message. Donc ma réponse peut être utile pour quelqu'un qui cherche une solution :)
Si vous n'avez qu'une ou deux colonnes qui utilisent le type d'énumération que vous souhaitez modifier, vous pouvez essayer ceci. Vous pouvez également modifier l'ordre des valeurs dans le nouveau 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 doit être répété s'il y a plus d'une colonne.
ALTER TYPE
. Mais même avant cela,ALTER TABLE foo ALTER COLUMN bar TYPE new_type USING bar::text::new_type;
était de loin supérieur.ALTER TABLE some_table ALTER COLUMN some_column TYPE some_enum_type USING some_column::text::some_enum_type;
PostgreSQL 9.1 introduit la capacité des types ALTER Enum:
la source
Une solution possible est la suivante; la condition préalable est qu'il n'y ait pas de conflits dans les valeurs énumérées utilisées. (Par exemple, lors de la suppression d'une valeur d'énumération, assurez-vous que cette valeur n'est plus utilisée.)
De cette manière également, l'ordre des colonnes ne sera pas modifié.
la source
pg_enum
qui peuvent réellement casser des choses et sont transactionnels, contrairement àALTER TYPE ... ADD
.default for column "my_column" cannot be cast automatically to type "my_enum"
. Vous devrez faire ce qui suit: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 vous tombez dans une situation où vous devez ajouter des
enum
valeurs dans la transaction, par exemple, exécutez-la lors de la migration de la voie de migration sur l'ALTER TYPE
instruction, vous obtiendrez une erreurERROR: ALTER TYPE ... ADD cannot run inside a transaction block
(voir problème de voie de migration # 350 ), vous pouvez ajouter ces valeurspg_enum
directement comme solution de contournement (type_egais_units
est le nom de la cibleenum
):la source
Compléter @Dariusz 1
Pour Rails 4.2.1, il y a cette section doc:
== Migrations transactionnelles
Si l'adaptateur de base de données prend en charge les transactions DDL, toutes les migrations seront automatiquement encapsulées dans une transaction. Il existe cependant des requêtes que vous ne pouvez pas exécuter dans une transaction, et pour ces situations, vous pouvez désactiver les transactions automatiques.
la source
Depuis la documentation Postgres 9.1 :
Exemple:
la source
Avis de non-responsabilité: je n'ai pas essayé cette solution, elle pourrait donc ne pas fonctionner ;-)
Vous devriez regarder
pg_enum
. Si vous voulez seulement changer l'étiquette d'un ENUM existant, une simple MISE À JOUR le fera.Pour ajouter de nouvelles valeurs ENUM:
pg_enum
. Si la nouvelle valeur doit être la dernière, vous avez terminé.pg_enum
dans l'ordre inverse.Illustration
Vous disposez du jeu d'étiquettes suivant:
et vous souhaitez obtenir:
puis:
puis:
Etc...
la source
Je n'arrive pas à poster un commentaire, donc je vais juste dire que la mise à jour de pg_enum fonctionne dans Postgres 8.4. Pour la façon dont nos énumérations sont configurées, j'ai ajouté de nouvelles valeurs aux types d'énumérations existants via:
C'est un peu effrayant, mais cela a du sens étant donné la façon dont Postgres stocke réellement ses données.
la source
La mise à jour de pg_enum fonctionne, tout comme l'astuce de colonne intermédiaire mise en évidence ci-dessus. On peut également utiliser USING magic pour changer directement le type de la colonne:
Tant que vous n'avez aucune fonction qui requiert ou renvoie explicitement cette énumération, vous êtes bon. (pgsql se plaindra lorsque vous supprimez le type, le cas échéant.)
Notez également que PG9.1 introduit une instruction ALTER TYPE, qui fonctionnera sur les énumérations:
http://developer.postgresql.org/pgdocs/postgres/release-9-1-alpha.html
la source
ALTER TABLE foo ALTER COLUMN bar TYPE test USING bar::text::new_type;
Mais largement hors de propos maintenant ...... USING bar::type
fonctionné pour moi. Je n'avais même pas besoin de préciser::text
.Le plus simple: se débarrasser des énumérations. Ils ne sont pas facilement modifiables et devraient donc très rarement être utilisés.
la source
Impossible d'ajouter un commentaire à l'endroit approprié, mais
ALTER TABLE foo ALTER COLUMN bar TYPE new_enum_type USING bar::text::new_enum_type
avec une valeur par défaut sur la colonne a échoué. J'ai dû:ALTER table ALTER COLUMN bar DROP DEFAULT
;et puis ça a marché.
la source
juste au cas où, si vous utilisez Rails et que vous avez plusieurs instructions, vous devrez les exécuter une par une, comme:
la source
Voici une solution plus générale mais plutôt rapide, qui à part changer le type lui-même met à jour toutes les colonnes de la base de données l'utilisant. La méthode peut être appliquée même si une nouvelle version d'ENUM est différente par plus d'une étiquette ou manque certaines des originales. Le code ci-dessous remplace
my_schema.my_type AS ENUM ('a', 'b', 'c')
parENUM ('a', 'b', 'd', 'e')
:L'ensemble du processus se déroulera assez rapidement, car si l'ordre des étiquettes persiste, aucun changement réel des données ne se produira. J'ai appliqué la méthode sur 5 tables en utilisant
my_type
et ayant 50 000 à 70 000 lignes dans chacune, et l'ensemble du processus n'a pris que 10 secondes.Bien sûr, la fonction renverra une exception au cas où des étiquettes manquantes dans la nouvelle version de l'ENUM sont utilisées quelque part dans les données, mais dans une telle situation, quelque chose devrait être fait au préalable de toute façon.
la source
Pour ceux qui recherchent une solution en transaction, ce qui suit semble fonctionner.
Au lieu de an
ENUM
, aDOMAIN
doit être utilisé sur typeTEXT
avec une contrainte vérifiant que la valeur se trouve dans la liste spécifiée des valeurs autorisées (comme suggéré par certains commentaires). Le seul problème est qu'aucune contrainte ne peut être ajoutée (et donc ni modifiée) à un domaine s'il est utilisé par n'importe quel type composite (la documentation dit simplement que cela "devrait éventuellement être amélioré"). Une telle restriction peut cependant être contournée en utilisant une contrainte appelant une fonction, comme suit.Auparavant, j'ai utilisé une solution similaire à la réponse acceptée, mais elle est loin d'être bonne une fois que les vues ou les fonctions ou les types composites (et en particulier les vues utilisant d'autres vues utilisant les ENUM modifiés ...) sont considérés. La solution proposée dans cette réponse semble fonctionner dans toutes les conditions.
Le seul inconvénient est qu'aucune vérification n'est effectuée sur les données existantes lorsque certaines valeurs autorisées sont supprimées (ce qui pourrait être acceptable, en particulier pour cette question). (Un appel à
ALTER DOMAIN test_domain VALIDATE CONSTRAINT val_check
aboutit à la même erreur que l'ajout d'une nouvelle contrainte au domaine utilisé par un type composite, malheureusement.)Notez qu'une légère modification telle que(ça marche, en fait - c'était mon erreur)CHECK (value = ANY(get_allowed_values()))
, lorsque laget_allowed_values()
fonction a renvoyé la liste des valeurs autorisées, ne fonctionnerait pas - ce qui est assez étrange, donc j'espère que la solution proposée ci-dessus fonctionne de manière fiable (elle le fait pour moi, jusqu'à présent ...).la source
Comme indiqué ci-dessus, la
ALTER
commande ne peut pas être écrite dans une transaction. La méthode suggérée consiste à insérer directement dans la table pg_enum, parretrieving the typelem from pg_type table
etcalculating the next enumsortorder number
;Voici le code que j'utilise. (Vérifie s'il existe une valeur en double avant l'insertion (contrainte entre enumtypid et enumlabel name)
Notez que votre nom de type est précédé d'un trait de soulignement dans la table pg_type. En outre, le nom de type doit être entièrement en minuscules dans la clause where.
Maintenant, cela peut être écrit en toute sécurité dans votre script de migration db.
la source
Je ne sais pas si j'ai une autre option mais nous pouvons laisser tomber la valeur en utilisant:
la source
Lorsque vous utilisez Navicat, vous pouvez accéder aux types (sous Affichage -> Autres -> Types) - obtenir la vue de conception du type - et cliquer sur le bouton "Ajouter une étiquette".
la source
ERROR: cannot drop type foo because other objects depend on it HINT: Use DROP ... CASCADE to drop the dependent objects too.