30

我使用 rails generate migrations 命令在我的 rails 应用程序中创建了一个表。这是迁移文件:

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

我期待列类型发生变化,但是 rake 被中止并出现以下错误消息。我想知道为什么这没有通过?我在我的应用程序中使用 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)

注意:该表没有数据。谢谢

4

6 回答 6

28

我引用手册关于ALTER TABLE

如果没有从旧类型到新类型的隐式或赋值强制转换,则必须提供 USING 子句。

你需要的是:

ALTER TABLE 列表 ALTER longitude TYPE integer USING longitude::int ;
ALTER TABLE 列表 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为该列定义了 a,则可能必须为新类型删除并重新创建它。

这是有关如何使用 ActiveRecord 执行此操作的博客文章
或者在评论中参考@mu 的建议。他知道他的红宝石。我只对这里的 PostgreSQL 好。

于 2012-04-27T01:03:41.037 回答
24

我知道这有点难看,但我更喜欢删除该列并使用新类型再次添加:

 def change
     remove_column :mytable, :mycolumn
     add_column :mytable, :mycolumn, :integer, default: 0
 end
于 2014-05-09T17:40:18.047 回答
24

我会将原始 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
于 2013-09-25T19:20:42.683 回答
12

以下是一个更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

这对我有用。

于 2016-04-27T19:37:27.333 回答
2
  1. 这些列中是否有现有数据?
  2. 您不应该将 int 用于纬度和经度。它们应该是浮点数。
于 2012-04-27T03:34:34.037 回答
0

纬度和经度是十进制的

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
于 2017-04-29T15:26:08.047 回答