[RoR] 用 BIGINT 作為 table primary key 的 type

不知道有沒有更漂亮的作法耶..

開發 Ruby on Rails 的人很多都會利用 ActiveRecord::Migration 來建立 database 中的 table,而 Migration 的好處是它幫你制定好了很多「資料型態」,讓你在寫 create table 的 script 時,不用特別考慮底層是用哪一套資料庫系統而煩惱 型態的問題。

而 Migration 中的 create_table 這個 method 會自動幫你的 table 設立一個 id 的欄位作為 primary key,若你的資料庫是採用 MySQL 的話,它的資料型態會使用 int(11) ,但若是想要把它改為使用 BIGINT (64-bit int) 的話,我自己的方式分成兩種:

  1. 動手 hack ActiveRecord::ConnectionAdapters::MysqlAdapter

    採用這個方式的話,在被你 hack 過的環境下,你的 migration scripts 不用作任何更改,因為我們直接換掉了 id 所採用的資料型態!找到 /lib/active_record/connection_adapters/mysql_adapter.rb 這個檔,然後:
    [code lang="ruby"]
    def native_database_types #:nodoc:
    {
    - :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
    + :primary_key => "bigint DEFAULT NULL auto_increment PRIMARY KEY",
    :string => { :name => "varchar", :limit => 255 },
    :text => { :name => "text" },
    :integer => { :name => "int", :limit => 11 },
    :float => { :name => "float" },
    :decimal => { :name => "decimal" },
    :datetime => { :name => "datetime" },
    :timestamp => { :name => "datetime" },
    :time => { :name => "time" },
    :date => { :name => "date" },
    :binary => { :name => "blob" },
    :boolean => { :name => "tinyint", :limit => 1 }
    }
    end
    [/code]
    上面的意思把 - 的那列換成 + 的這一列,存檔後,以後你在使用 Migration 來 create_table 時都會使用 BIGINT 作為 primary key 的資料列態了。

  2. 改寫 Migration script

    上述的作法雖然簡捷,但是 code 的可攜性就不高了,一旦 deploy 到別的環境,又要手動 hack 一遍,所以直接寫在 migration script 裡,雖然麻煩但 code 就可以帶著走了。

    假設原本的 script 是寫成這樣:
    [code lang="ruby"]
    create_table :users do |t|
    t.column :userid, :string
    t.column :passwd, :string
    end
    [/code]
    那可以改成:
    [code lang="ruby"]
    create_table :users, :id => false do |t|
    t.column :userid, :string
    t.column :passwd, :string
    end
    execute "alter table users add column id bigint DEFAULT NULL auto_increment PRIMARY KEY"
    [/code]
    讓它在建 table 時先不用預設的方式產生 id 欄位,而後再用 SQL 的 alter table 指令來生 id 欄位。

如果有人有更好的作法,歡迎指教,謝謝。

  • base on 你的寫法,我做了一點小小修改↓

    create_table :accounts do |t|
    t.string :password
    t.string :name
    t.timestamp :birthday
    t.timestamps
    end
    execute “alter table accounts modify id varchar(32) not null”

    我的做法是還是讓Ruby自動create id,然後再去修改id欄位的data type
    本例是要把id改為varchar(32) not null的data type(不用加primary key)
    因為我比較習慣primary key欄位擺在table的第一個 😛

    不過我比較過兩個寫法,你的寫法run的速度會快一點 😛
    == 2 CreateAccounts: migrating ==============================
    — create_table(:accounts)
    -> 0.0790s
    — execute(“alter table accounts modify id varchar(32) not null”)
    -> 0.1560s

    == 2 CreateAccounts: migrating ==============================
    — create_table(:accounts, {:id=>false})
    -> 0.0780s
    — execute(“alter table accounts add column id varchar(32) not null primary key”)
    -> 0.1100s

    不過以coding要key的字數來說,我的寫法會比較短一些 XD

  • 不過我覺得應該要有更漂亮的做法才對
    可是上Ruby on Rails Talk的群組裡找沒有
    有人是說id用serial的data type是Rails的慣例
    所以不要將它的data type改變比較好… @@

  • colin

    create_table :table_test2, :force=>true, :id=>false do |t|
    t.column :test_id, :primary_key
    t.column :name, :string, :limit=>512
    end
    change_column :table_test2, :test_id, :number
    这样做也可以的,我是使用oracle的。
    直接写sql语句,可能在不同的数据库上运行有兼容性的问题。