2012-04-27 18 views
24

Creé una tabla en mi aplicación de rieles con el comando rails generate migrations. Aquí es que el archivo de migración:Migraciones de rieles: intenté cambiar el tipo de columna de cadena a entero

class CreateListings < ActiveRecord::Migration 
    def change 
    create_table :listings do |t| 
     t.string :name 
     t.string :telephone 
     t.string :latitude 
     t.string :longitude 

     t.timestamps 
    end 
    end 
end 

luego quería para almacenar la latitud y longitud como enteros de modo Traté de correr:

rails generate migration changeColumnType 

y el contenido de ese archivo son:

class ChangeColumnType < ActiveRecord::Migration 
    def up 
    #change latitude columntype from string to integertype 
    change_column :listings, :latitude, :integer 
    change_column :listings, :longitude, :integer 
    #change longitude columntype from string to integer type 
    end 

    def down 
    end 
end 

Esperaba que el tipo de columna cambiara, pero se canceló el rake y apareció el siguiente mensaje de error. Me preguntaba por qué esto no pasó? Estoy usando postgresql en mi aplicación.

rake db:migrate 
== ChangeColumnType: migrating =============================================== 
-- change_column(:listings, :latitude, :integer) 
rake aborted! 
An error has occurred, this and all later migrations canceled: 

PG::Error: ERROR: column "latitude" cannot be cast to type integer 
: ALTER TABLE "listings" ALTER COLUMN "latitude" TYPE integer 

Tasks: TOP => db:migrate 
(See full trace by running task with --trace) 

NOTA: La tabla no tiene DATOS. Gracias

+0

Asegúrese de que tiene datos sobre ella y usted podría intentar hacer un rollback –

+3

Si no hay datos que puede simplemente eliminar las columnas y volver a añadirlos con el tipo correcto. Un grado completo de latitud y longitud es bastante grande, por lo que es posible que desee pensar en qué tipo realmente desea para esas columnas. –

Respuesta

23

cito el manual about ALTER TABLE:

Una cláusula USING debe proporcionarse si no hay implícita o cesión elenco de viejo a nuevo tipo.

Lo que necesita es:

 
ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int; 
ALTER TABLE listings ALTER latitude TYPE integer USING latitude::int; 

o más corto y más rápido (para tablas grandes) en un solo comando:

ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int 
        ,ALTER latitude TYPE integer USING latitude::int; 

Este funciona con o sin datos siempre y cuando todos las entradas son convertibles a integer.
Si ha definido DEFAULT para la columna, puede que tenga que soltar y volver a crear eso para el nuevo tipo.

Aquí está blog article on how to do this with ActiveRecord.
O vaya con el consejo de @mu en el comentario. Él conoce a su Ruby. Solo estoy bien con el PostgreSQL aquí.

+4

Para una sola vez, lo más fácil es envolver el SQL en un 'connection.execute ('...')' en lugar de rebuscar con el parche de mono. –

+1

Para la solución en la migración de raíles, consulte aquí: http://stackoverflow.com/questions/10690289/rails-gmaps4rails-gem-on-postgres – cintrzyk

2
  1. ¿Tiene datos existentes en esas columnas?
  2. No debe usar int para latitud y longitud. Deberían estar en puntos flotantes en su lugar.
+0

Utilizando un número entero o largo en (por ejemplo) grados * 10^12 de latitud/long puede ser mucho más rápido y más eficiente en almacenamiento si conoce de antemano la resolución que necesita. Sin embargo, PITA puede trabajar con él y es propenso a errores de conversión a menos que todos sus algoritmos puedan trabajar con él de forma nativa en esa forma. Estoy de acuerdo en que el doble o flotar es más seguro a menos que sepa que necesita algo diferente y tiene buenas razones. –

+0

¿Qué hay de malo con almacenarlos como cadenas? – banditKing

+0

Aquí está la explicación y recomendación de Google Maps: https://developers.google.com/maps/articles/phpsqlajax#createtable – Victor

21

Incluiría el SQL sin procesar en su archivo de migración como se muestra a continuación para que actualice schema.rb.

class ChangeColumnType < ActiveRecord::Migration 
    def up 
    execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE integer USING (latitude::integer)' 
    execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE integer USING (longitude::integer)' 
    end 

    def down 
    execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE text USING (latitude::text)' 
    execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE text USING (longitude::text)' 
    end 
end 
+0

Tenga en cuenta que el ALTER debe ser varchar en lugar de texto para que coincida con el tipo de cadena. – Martin

17

Sé que este un poco feo, pero yo prefiero a eliminar sólo la columna y añadir de nuevo con el nuevo tipo:

def change 
    remove_column :mytable, :mycolumn 
    add_column :mytable, :mycolumn, :integer, default: 0 
end 
+0

Ejecuté una migración change_column: mytable,: mycolumn,: float. Luego ejecuté un par de migraciones más, lo envié a github, lo llevé al heroku solo para obtener este error. Ejecuté una nueva migración como sugiere arriba, pero el error aún persiste debido a la migración anterior. Ahora no puedo deshacer porque remove_column es irreversible. ¿Soy fubar? – tbone

+0

Actualización al comentario anterior: eliminé la migración anterior con 'rails destroy migration migration_name' luego rake db: migrate y ahora todo está bien. – tbone

7

La siguiente es una más rails way de abordar el problema. Para mi caso, tenía dos columnas en mi tabla de compras que necesitaba convertir de tipo cadena a flotante.

def change 
    change_column :purchases, :mc_gross, 'float USING CAST(mc_gross AS float)' 
    change_column :purchases, :mc_fee, 'float USING CAST(mc_fee AS float)' 
end 

Eso me sirvió de algo.

+0

Esto funcionó muy bien para mí, gracias. –

0

latitud y longitud es decimal

rails g scaffold client name:string email:string 'latitude:decimal{12,3}' 'longitude:decimal{12,3}' 

class CreateClients < ActiveRecord::Migration[5.0] 
    def change 
    create_table :clients do |t| 
     t.string :name 
     t.string :email 
     t.decimal :latitude, precision: 12, scale: 3 
     t.decimal :longitude, precision: 12, scale: 3 

     t.timestamps 
    end 
    end 
end 
Cuestiones relacionadas