Rails Migrations: пытался изменить тип столбца со строкового на целочисленный

Я создал таблицу в своем приложении rails с помощью команды rails generate migrations. Вот этот файл миграции:

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

Затем я хотел сохранить широту и долготу как целые числа, поэтому я попытался запустить:

rails generate migration changeColumnType

и содержимое этого файла:

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

Я ожидал, что тип столбца изменится, однако грабли были прерваны, и появилось следующее сообщение об ошибке. Мне было интересно, почему это не прошло? Я использую postgresql в своем приложении.

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)

ПРИМЕЧАНИЕ. В таблице нет ДАННЫХ. Спасибо


person banditKing    schedule 27.04.2012    source источник
comment
Убедитесь, что у вас нет данных об этом, и вы можете попробовать сделать откат   -  person Ismael    schedule 27.04.2012
comment
Если данных нет, вы можете просто удалить столбцы и повторно добавить их с правильным типом. Целый градус широты/долготы довольно велик, поэтому вы можете подумать о том, какой тип вы действительно хотите для этих столбцов.   -  person mu is too short    schedule 27.04.2012


Ответы (6)


Я цитирую руководство о ALTER TABLE:

Предложение USING должно быть предоставлено, если нет неявного преобразования или преобразования присваивания из старого типа в новый.

Что вам нужно:

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

Или короче и быстрее (для больших таблиц) одной командой:

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

Это работает с данными или без них, если все записи могут быть преобразованы в integer.
Если вы определили DEFAULT для столбца, возможно, вам придется удалить его и создать заново для нового типа.

Вот статья в блоге о том, как это сделать с помощью ActiveRecord..
Или воспользуйтесь советом @mu в комментарии. Он знает свой Рубин. У меня хорошо получается только с PostgreSQL.

person Erwin Brandstetter    schedule 27.04.2012
comment
Для одноразового использования проще всего обернуть SQL в connection.execute('...'), а не возиться с исправлением обезьяны. - person mu is too short; 27.04.2012
comment
Решение о миграции на рельсы см. здесь: stackoverflow. ком/вопросы/10690289/ - person cintrzyk; 23.09.2013

Я знаю, что это немного некрасиво, но я предпочитаю просто удалить столбец и снова добавить новый тип:

 def change
     remove_column :mytable, :mycolumn
     add_column :mytable, :mycolumn, :integer, default: 0
 end
person rizidoro    schedule 09.05.2014
comment
Я запустил миграцию change_column :mytable, :mycolumn, :float. Затем запустил еще пару миграций, запушил на github, запушил на героку только для того, чтобы получить эту ошибку. Я выполнил новую миграцию, как вы предложили выше, но ошибка все еще сохраняется из-за предыдущей миграции. Теперь я не могу откатиться, потому что remove_column необратим. Я фубар? - person tomb; 06.02.2018
comment
Обновление к предыдущему комментарию: я удалил предыдущую миграцию с помощью «rails destroy migration migration_name», затем rake db: migrate, и теперь все в порядке. - person tomb; 06.02.2018
comment
после нескольких часов попыток это помогло мне преобразовать логическое значение в перечисление - person Jose Kj; 12.12.2018

Я бы включил необработанный SQL в ваш файл миграции, как показано ниже, чтобы он обновил 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
person neverbendeasy    schedule 25.09.2013
comment
Обратите внимание, что нижний ALTER должен быть varchar вместо текста, чтобы соответствовать типу строки. - person Martin; 24.06.2014

Ниже приводится более rails way подход к проблеме. В моем случае у меня было два столбца в таблице покупок, которые мне нужно было преобразовать из строки типа в число с плавающей запятой.

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

Это помогло мне.

person Joseph N.    schedule 27.04.2016
comment
Хотя хорошо знать, как вмешиваться в postgreSQL, можно потерять след такого изменения. Этот рельсовый путь оставляет след для операции. - person Jerome; 20.03.2020

  1. У вас есть существующие данные в этих столбцах?
  2. Вы не должны использовать int для широты и долготы. Вместо этого они должны быть с плавающей запятой.
person Victor    schedule 27.04.2012
comment
Использование целого числа или долготы в (например) градусах*10^12 широты/долготы может быть намного быстрее и эффективнее для хранения, если вы заранее знаете необходимое разрешение. Однако с PITA можно работать, и он подвержен ошибкам преобразования, если только все ваши алгоритмы не могут работать с ним изначально в этой форме. Я согласен с тем, что double или float безопаснее, если вы не знаете, что вам нужно что-то другое и у вас есть веские причины. - person Craig Ringer; 27.04.2012
comment
Вот объяснение и рекомендация Google Maps: developers.google.com/maps/articles/phpsqlajax# таблица создания - person Victor; 27.04.2012

широта и долгота десятичные

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
person gilcierweb    schedule 29.04.2017