Changer le type du champ varchar en entier: "ne peut pas être converti automatiquement en type entier"

154

J'ai une petite table et un certain champ contient le type " caractère variant ". J'essaie de le changer en " Integer " mais cela donne une erreur indiquant que la conversion n'est pas possible.

Y a-t-il un moyen de contourner cela ou devrais-je simplement créer une autre table et y apporter les enregistrements à l'aide d'une requête.

Le champ contient uniquement des valeurs entières.

itsols
la source
Quel ALTER TABLE spécifique avez-vous essayé et quel était le message d'erreur spécifique?
mu est trop court
@muistooshort J'ai essayé d'utiliser alter de phppgadmin. Sélectionnez la colonne et essayez de saisir le nouveau type de champ. L'erreur est:SQL error: ERROR: column "MID" cannot be cast to type integer
itsols
3
Le premier est de sauvegarder la table. Ensuite, vous pouvez créer une autre colonne (disons field2) de type entier dans la même table. Sélectionnez la conversion en valeur entière du champ1 en champ2. Puis renommez la colonne.
Igor
@Igor mais la nouvelle colonne tombe à la fin du tableau, non? Ne puis-je pas l'avoir dans la même position?
itsols
2
@itsols Se soucier des positions des colonnes est généralement le signe d'une conception d'application douteuse. Vous souhaitez presque toujours utiliser des colonnes et des SELECTlistes nommées explicitement , sans vous fier aux positions ordinales des colonnes. Cela dit, l'approche donnée dans les réponses préservera la position de la colonne.
Craig Ringer

Réponses:

264

Il n'y a pas de conversion implicite (automatique) de textou varcharvers integer(c'est- à -dire que vous ne pouvez pas passer un varcharà une fonction attendue integerou affecter un varcharchamp à un champ integer), vous devez donc spécifier un cast explicite en utilisant ALTER TABLE ... ALTER COLUMN ... TYPE. .. UTILISATION :

ALTER TABLE the_table ALTER COLUMN col_name TYPE integer USING (col_name::integer);

Notez que vous pouvez avoir des espaces dans vos champs de texte; dans ce cas, utilisez:

ALTER TABLE the_table ALTER COLUMN col_name TYPE integer USING (trim(col_name)::integer);

pour supprimer l'espace blanc avant la conversion.

Cela devrait être évident à partir d'un message d'erreur si la commande a été exécutée psql, mais il est possible que PgAdmin-III ne vous montre pas l'erreur complète. Voici ce qui se passe si je le teste psqlsur PostgreSQL 9.2:

=> CREATE TABLE test( x varchar );
CREATE TABLE
=> insert into test(x) values ('14'), (' 42  ');
INSERT 0 2
=> ALTER TABLE test ALTER COLUMN x TYPE integer;
ERROR:  column "x" cannot be cast automatically to type integer
HINT:  Specify a USING expression to perform the conversion. 
=> ALTER TABLE test ALTER COLUMN x TYPE integer USING (trim(x)::integer);
ALTER TABLE        

Merci @muistooshort d'avoir ajouté le USINGlien.

Voir aussi cette question connexe ; il s'agit de migrations Rails, mais la cause sous-jacente est la même et la réponse s'applique.

Si l'erreur persiste, elle peut ne pas être liée aux valeurs de colonne, mais les index sur cette colonne ou les valeurs par défaut de cette colonne peuvent échouer le typage. Les index doivent être supprimés avant ALTER COLUMN et recréés après. Les valeurs par défaut doivent être modifiées de manière appropriée.

Craig Ringer
la source
Je vous remercie d'avoir pris le temps. Mais je n'arrive pas à faire fonctionner cela. J'ai essayé votre ligne ALTER et cela me donne une erreur "Erreur de syntaxe près de l'utilisation"
itsols
Ma déclaration: ALTER TABLE "tblMenus" ALTER COLUMN "MID" USING (trim ("MID") :: integer);
itsols
1
@itsols Entièrement mon erreur; Je l'ai corrigé comme j'ai vu votre commentaire. Voir révisé. C'était juste dans le code de démonstration, mais pas dans l'exemple générique du début.
Craig Ringer
Mille mercis! Cette réponse m'a épargné beaucoup d'ennuis et de temps. Je me demande pourquoi niether phppgadmin ni pgadmin n'ont cela comme fonctionnalité ...
itsols
@itsols La plupart des membres de l'équipe principale ne sont pas intéressés par PgAdmin, et peu d'entre eux l'utilisent. Il a des verrues d'utilisabilité ennuyeuses et des limitations de fonctionnalités. Ce n’est qu’un d’entre eux. Parce que peu d'experts utilisent PgAdmin, ils ne sont pas aussi motivés pour réparer les choses qui les ennuieraient à ce sujet. Je ne l'utilise pas moi-même, car je trouve psqlbeaucoup plus rapide et plus facile. J'ai écrit un peu de diatribe sur la convivialité de PgAdmin en ce qui concerne la sauvegarde et la restauration il y a quelque temps: blog.ringerc.id.au/2012/05
Craig Ringer
71

cela a fonctionné pour moi.

changer la colonne varchar en int

change_column :table_name, :column_name, :integer

eu:

PG::DatatypeMismatch: ERROR:  column "column_name" cannot be cast automatically to type integer
HINT:  Specify a USING expression to perform the conversion.

chnged to

change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)'
Bibangamba
la source
avez-vous essayé cet exercice avec des données et vos données étaient-elles intactes?
itsols
3
tant que ce qui est dans la colonne est un entier, oui
bibangamba
Ça ne marche pas avec moi. J'utilise ruby ​​2.2.3 avec des rails 4.2.3
Thinh D.Bui
@ ThinhD.Bui - Fonctionne pour moi, 2.3.0, rails 4.2.6
Philip
1
Faites également attention aux valeurs par défaut
Francisco Quintero
17

Vous pouvez le faire comme:

change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)'

ou essayez ceci:

change_column :table_name, :column_name, :integer, using: 'column_name::integer'

Si vous souhaitez en savoir plus sur ce sujet, lisez cet article: https://kolosek.com/rails-change-database-column

Nesha Zoric
la source
8

Essayez ceci, cela fonctionnera à coup sûr.

Lors de l'écriture de migrations Rails pour convertir une colonne de chaîne en un entier, vous diriez généralement:

change_column :table_name, :column_name, :integer

Cependant, PostgreSQL se plaindra:

PG::DatatypeMismatch: ERROR:  column "column_name" cannot be cast automatically to type integer
HINT:  Specify a USING expression to perform the conversion.

Le "conseil" vous indique essentiellement que vous devez confirmer que vous voulez que cela se produise, et comment les données doivent être converties. Dites simplement ceci dans votre migration:

change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)'

Ce qui précède imite ce que vous savez des autres adaptateurs de base de données. Si vous avez des données non numériques, les résultats peuvent être inattendus (mais vous convertissez en entier, après tout).

Subhash Chandra
la source
Je voulais juste ajouter un autre point, soyez prudent avec change_column. c'est irréversible. Je suggère d'utiliser le haut et le bas dans la migration pour rendre cela réversible.
Mukesh Kumar Gupta
2
PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: ""erreur se produit
Shaig Khaligli
6

J'ai le même problème. Ensuite, j'ai réalisé que j'avais une valeur de chaîne par défaut pour la colonne que j'essayais de modifier. La suppression de la valeur par défaut a fait disparaître l'erreur :)

Valdenir Antoglioli Junior
la source
Les index existants sur cette colonne peuvent également poser problème. Ils doivent être supprimés avant ALTER et recréés après.
Envek
1

Si vous avez accidentellement ou non mélangé des entiers avec des données texte, vous devez d'abord exécuter la commande de mise à jour ci-dessous (si ce n'est pas au-dessus, la modification de la table échouera):

UPDATE the_table SET col_name = replace(col_name, 'some_string', '');
webrama.pl
la source
3
Vous seriez mieux avec quelque chose comme regexp_replace(col_name, '[^0-9.]','','g')si vous essayez de supprimer les caractères indésirables et les espaces blancs. Vous auriez besoin de quelque chose d'un peu plus sophistiqué si vous voulez conserver NaNet Infet 10E42la notation scientifique, cependant.
Craig Ringer
1

Si vous travaillez sur un environnement de développement (ou sur un environnement de production. Cela peut être une sauvegarde de vos données), effacez d'abord les données du champ DB ou définissez la valeur sur 0.

UPDATE table_mame SET field_name= 0;

Après cela, pour exécuter la requête ci-dessous et après avoir exécuté avec succès la requête, vers le schéma de migration et après cela, exécutez le script de migration.

ALTER TABLE table_mame ALTER COLUMN field_name TYPE numeric(10,0) USING field_name::numeric;

Je pense que cela vous aidera.

Sandip Rajput
la source
1

J'ai eu le même problème. J'ai commencé à réinitialiser la valeur par défaut de la colonne.

change_column :users, :column_name, :boolean, default: nil
change_column :users, :column_name, :integer, using: 'column_name::integer', default: 0, null: false
Maxime Boué
la source