PostgreSQL的nextval如何生成现有值?

6

我不得不将一个基于mySql的ruby on rails应用程序迁移到使用postgresql。目前只有一个问题,我不知道如何解决它。

数据迁移带来了id,但postgresql现在存在与现有id相关的问题:我不清楚它获取用于确定nextval基础的值的位置。虽然您可能认为这是个好主意,但它肯定不是该列中最高值。无论如何,它现在与现有id值发生冲突。id列是从标准RoR迁移创建的,并定义为:

not null default nextval('geopoints_id_seq'::regclass)

有没有一些地方可以黑客攻击其使用的基础值?现在这个问题可能出现在20个左右的表格中:我可以使用

'select max(id) from <table_name>' 

但是这似乎使得自动递增列的想法毫无意义。

最好如何处理?

5个回答

11

Postgres适配器上有一个reset_pk_sequences!方法。您可以调用它,它会将其设置为max(id)+1,这可能是您想要的。

在一些项目中,我经常获得数据ETL,以保证rake任务对所有模型进行此操作或针对指定模型进行此操作。以下是该任务-将其包含在某个Rakefile中或在其自己的lib/tasks下:

desc "Reset all sequences. Run after data imports"
task :reset_sequences, :model_class, :needs => :environment do |t, args|
  if args[:model_class]
    classes = Array(eval args[:model_class])
  else
    puts "using all defined active_record models"
    classes = []
    Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each { |file| require file }
    Object.subclasses_of(ActiveRecord::Base).select { |c|
      c.base_class == c}.sort_by(&:name).each do |klass|
        classes << klass
      end
  end
  classes.each do |klass|
      next if klass == CGI::Session::ActiveRecordStore::Session && ActionController::Base.session_store.to_s !~ /ActiveRecordStore/

        puts "reseting sequence on #{klass.table_name}"
        ActiveRecord::Base.connection.reset_pk_sequence!(klass.table_name)
    end
end

现在,您可以通过使用rake reset_sequences来运行所有模型(在RAIS_ROOT / app / models下定义),也可以通过传入类名来运行特定模型。


哇,非常感谢大家的及时和有用的回答。我会尝试@hgimenez的解决方案,因为我在Rails环境中,但我认为这个信息是我可以通过postgres命令行来完成的。作为后续:我将尝试这样做,但我能把这样的语句放到迁移中吗? - Dan Donaldson
肯定的,你可以进行这样的迁移:ActiveRecord::Base.connection.reset_pk_sequence!('table_name'),但这当然也可以从 psql 中完成。 - hgmnz

5
Rails 3版本的代码如下所示:
namespace :db do
  desc "Reset all sequences. Run after data imports"
  task :reset_sequences, :model_class, :needs => :environment do |t, args|
    if args[:model_class]
      classes = Array(eval args[:model_class])
    else
      puts "using all defined active_record models"
      classes = []
      Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each { |file| require file }
      ActiveRecord::Base.subclasses.select { |c|c.base_class == c}.sort_by(&:name).each do |klass|
        classes << klass
      end
    end
    classes.each do |klass|
      puts "reseting sequence on #{klass.table_name}"
      ActiveRecord::Base.connection.reset_pk_sequence!(klass.table_name)
    end
  end
end

https://gist.github.com/909032


2
我认为RAILS_ROOT应该改为Rails.root.to_s。另外,那个task语法已经过时了。我认为应该是task :reset_sequences, [:model_class] => [:environment] - Jared Beck

3

根据这个定义,该列将从geopoints_id_seq序列中获取下一个值。该序列不直接附加到表上。如果您正在迁移数据,则必须创建或更新该序列,以便其起始点大于表中当前最大的id。

您应该能够使用以下方法设置其新值:

   ALTER SEQUENCE geopoints_id_seq RESTART with 1692;

或者,无论是从哪个表中选择最大的id;


1

0
使用setval()来设置序列的起始值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接