You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails项目如何将integer类型主键及关联字段全部改为bigint

解决方案

1. 列举所有integer类型主键的方法

方法1:Rails控制台批量查询

直接在项目控制台执行以下代码,可遍历所有模型匹配integer类型主键:

# 预加载所有模型
Rails.application.eager_load!

integer_pk_tables = []
ActiveRecord::Base.descendants.each do |model|
  next if model.abstract_class? || model.table_name.blank?

  pk_column = model.columns_hash[model.primary_key]
  if pk_column&.type == :integer && pk_column&.sql_type != 'bigint'
    integer_pk_tables << { table: model.table_name, primary_key: model.primary_key, type: pk_column.sql_type }
  end
end

# 输出结果
pp "Integer类型主键的表清单:"
pp integer_pk_tables

方法2:直接通过数据库SQL查询

  • PostgreSQL版本:
SELECT
  tc.table_name,
  kcu.column_name AS primary_key,
  c.data_type
FROM
  information_schema.table_constraints tc
  JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
  JOIN information_schema.columns c ON c.table_name = tc.table_name AND c.column_name = kcu.column_name
WHERE
  tc.constraint_type = 'PRIMARY KEY'
  AND c.data_type = 'integer'
ORDER BY tc.table_name;
  • MySQL版本:
SELECT
  TABLE_NAME AS table_name,
  COLUMN_NAME AS primary_key,
  DATA_TYPE
FROM
  INFORMATION_SCHEMA.COLUMNS
WHERE
  TABLE_SCHEMA = DATABASE()
  AND COLUMN_KEY = 'PRI'
  AND DATA_TYPE = 'int';

2. 批量修改迁移脚本

注意:迁移前务必全量备份数据库,大表建议在业务低峰期执行,避免锁表影响线上服务
首先生成迁移文件:
rails g migration convert_int_pks_and_fks_to_bigint
然后替换迁移文件内容为以下代码:

class ConvertIntPksAndFksToBigint < ActiveRecord::Migration[5.1]
  disable_ddl_transaction! # 大表场景使用非事务模式,避免长事务锁库

  def up
    # 第一步:转换所有integer类型主键为bigint
    integer_pk_tables.each do |table, pk|
      # PostgreSQL场景请删除auto_increment参数
      change_column table, pk, :bigint, auto_increment: true
    end

    # 第二步:转换所有显式声明的外键
    connection.foreign_keys.each do |fk|
      from_column = fk.column
      from_table = fk.from_table
      column_type = connection.columns(from_table).find { |c| c.name == from_column }&.sql_type
      next unless column_type.in?(%w[int integer])

      # 先删除外键约束,修改字段类型后再重建约束
      remove_foreign_key from_table, name: fk.name
      change_column from_table, from_column, :bigint
      # MySQL场景可加algorithm: :inplace减少锁表时间
      add_foreign_key from_table, fk.to_table, column: from_column, name: fk.name
    end

    # 第三步:转换未声明外键、符合xxx_id命名规范的关联字段
    connection.tables.each do |table|
      connection.columns(table).each do |col|
        next unless col.name.end_with?('_id') && col.sql_type.in?(%w[int integer]) && col.name != connection.primary_key(table)
        change_column table, col.name, :bigint
      end
    end
  end

  def down
    # 不建议回滚,如有需要可基于备份恢复数据
    raise ActiveRecord::IrreversibleMigration
  end

  private

  # 提前获取所有integer类型主键的表
  def integer_pk_tables
    @integer_pk_tables ||= begin
      res = []
      connection.tables.each do |table|
        pk = connection.primary_key(table)
        next unless pk
        col = connection.columns(table).find { |c| c.name == pk }
        res << [table, pk] if col&.sql_type.in?(%w[int integer])
      end
      res
    end
  end
end

执行迁移即可完成批量转换:
rails db:migrate

额外注意事项

  • 如果使用MySQL < 8.0或者MariaDB版本,修改主键会触发表重建,10G以上大表建议使用pt-online-schema-change这类无锁变更工具执行
  • 迁移完成后可重新执行第一部分的查询脚本,验证所有integer类型的主键、关联字段都已完成转换

内容的提问来源于stack exchange,提问作者oma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:09:02