【问题标题】:postgresql nextval generating existing valuespostgresql nextval 生成现有值
【发布时间】:2010-12-15 03:31:43
【问题描述】:

我不得不从基于 mySql 的 ruby​​ on rails 应用程序迁移到使用 postgresql。到目前为止没有问题,但我不知道如何解决它。

数据的迁移带来了 id,而 postgresql 现在遇到了现有 id 的问题:我不清楚它从哪里获得用于确定 nextval 基数的值:它肯定不是最高的列中的值,尽管您可能认为这是一个好主意。无论如何,它现在正在与现有的 id 值发生冲突。从标准 RoR 迁移创建的 id 列定义为

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

它用作基础的值是否存在可以被黑客入侵的地方?这个问题现在可能出现在 20 个左右的表中:我可以使用

'select max(id) from <table_name>' 

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

如何最好地处理?

【问题讨论】:

    标签: ruby-on-rails postgresql auto-increment nextval


    【解决方案1】:

    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

    【讨论】:

    • 我认为RAILS_ROOT 应该是Rails.root.to_s。此外,不推荐使用 task 语法。我觉得应该是task :reset_sequences, [:model_class] =&gt; [:environment]
    【解决方案2】:

    Postgres adapter 上有一个 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 中的命令行来完成。作为后续:我要试试这个,但是我可以把这样的声明放到迁移中吗?
    • 当然,您可以进行如下迁移:ActiveRecord::Base.connection.reset_pk_sequence!('table_name'),但这当然也可以从 psql 完成。
    • 不错的一个。感谢您的帮助。
    【解决方案3】:

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

    您应该能够使用例如设置其新值

       ALTER SEQUENCE geopoints_id_seq RESTART with 1692;
    

    或者从 table_name 中选择 max(id);产量

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        PG 使用序列:

        像这样使它的当前值比表中的最高值高 1。

        SELECT setval('geopoints_id_seq', 999999999, true);
        

        还可以查看这些

        http://www.postgresql.org/docs/8.4/interactive/datatype-numeric.html#DATATYPE-SERIAL

        http://www.postgresql.org/docs/8.4/interactive/functions-sequence.html

        【讨论】:

          猜你喜欢
          • 2022-10-07
          • 1970-01-01
          • 1970-01-01
          • 2011-01-12
          • 2010-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多