使用 postgresql 恢复 rails 4 上的自动递增 id

Restore auto-increment id on rails 4 with postgresql

我需要将一些数据从旧网站迁移到新 rails 网站。我需要保留我的城市 ID,所以我创建了我的 table 并将 id 设置为 false 并手动创建了该列:

create_table "cities", id: false, force: true do |t|
   t.integer  "id", null: false
   t.string   "name", null: false
   t.datetime "created_at"
   t.datetime "updated_at"
   t.boolean  "is_active",  default: true, null: false
   t.string   "code_che"
end

add_index "cities", ["id"], name: "index_cities_on_id", unique: true, using: :btree

完成迁移后,我希望能够创建新城市,例如 City.create(name: 'My big city') 但出现下一个错误:

ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR:  null value in column "id" violates not-null constraint

很明显我弄错了,你能帮我看看在哪里吗?我正在使用 rails 4 和 postgresql gem.

谢谢。

可能有更多 Rails-y 的方法来做到这一点,但是直接使用 SQL 你需要这样做。将“12345”替换为比 'id'.

当前最大值大一的值
CREATE SEQUENCE cities_id_seq START WITH 12345;
ALTER TABLE cities ALTER COLUMN id SET DEFAULT 'nextval(''cities_id_seq''::regclass)';
ALTER TABLE cities ALTER COLUMN id SET NOT NULL;

您可以像这样将其放入 Rails 迁移中:

def up
  execute %Q{
    CREATE SEQUENCE cities_id_seq START WITH 12345;
    ALTER TABLE cities ALTER COLUMN id SET DEFAULT 'nextval(''cities_id_seq''::regclass)';
    ALTER TABLE cities ALTER COLUMN id SET NOT NULL;
  }
end

P.S。下次您可以将序列创建留给 rails 并且在迁移数据时直接设置 id 值。然后当你完成时,更新序列的下一个值是一件简单的事情。

首先,关闭现有的 cities table(当然,假设您还没有向其中添加任何新数据)。然后以正常方式创建 cities

create_table "cities", force: true do |t|
   t.string   "name", null: false
   t.datetime "created_at"
   t.datetime "updated_at"
   t.boolean  "is_active",  default: true, null: false
   t.string   "code_che"
end

这将为您提供一个正常的 cities table 并以 id 作为主键,并且 id 将从序列中获得值。

接下来导入现有数据。这将为您提供一堆 id 值,而 cities.id 后面的序列不知道这些值,因此如果您尝试插入新城市,将会遇到问题。这很容易解决,使用 PostgreSQL 的 setval function 在迁移中用一点 SQL 调整序列:

execute(%q{
  select setval('cities_id_seq', (select max(id) from cities))
})

当您让 Rails 为 table 创建 id PK 时,它会为 id 使用 serial 列。 serial 列或多或少是具有提供默认值的序列的 integer 列。对于名为 X 的 table,序列的名称将为 X_id_seq,因此上面的名称为 cities_id_seq。然后您想将序列的当前值设置为最大现有 cities.id 值,以便序列中的 next 值将比该最大值大 1。