Rails在id字段上重置种子的方法

时间:2010-01-19 21:01:36

标签: sql ruby-on-rails database

我找到了这个问题的“纯SQL”答案。是否有一种方法, 在Rails ,重置特定表的id字段?
我为什么要这样做?因为我有不断移动数据的表 - 很少超过100行,但总是不同。它现在高达25k,而且没有任何意义。我打算使用Rails应用程序内部的调度程序(rufus-scheduler)来每月运行id字段重置。

9 个答案:

答案 0 :(得分:120)

您从未提及您正在使用的DBMS。如果这是postgreSQL,ActiveRecord postgres适配器有一个reset_pk_sequences!方法可以使用:

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')

答案 1 :(得分:55)

我根据hgimenez的回答和this other one提出了解决方案。

因为我经常使用Sqlite或PostgreSQL,所以我只为那些开发;但是,将它扩展到MySQL,应该不会太麻烦。

将它放在lib /中并在初始化程序中需要它:

# lib/active_record/add_reset_pk_sequence_to_base.rb
module ActiveRecord
  class Base
    def self.reset_pk_sequence
      case ActiveRecord::Base.connection.adapter_name
      when 'SQLite'
        new_max = maximum(primary_key) || 0
        update_seq_sql = "update sqlite_sequence set seq = #{new_max} where name = '#{table_name}';"
        ActiveRecord::Base.connection.execute(update_seq_sql)
      when 'PostgreSQL'
        ActiveRecord::Base.connection.reset_pk_sequence!(table_name)
      else
        raise "Task not implemented for this DB adapter"
      end
    end     
  end
end

用法:

Client.count # 10
Client.destroy_all
Client.reset_pk_sequence
Client.create(:name => 'Peter') # this client will have id=1

编辑:由于您最常见的情况是清除数据库表后,我建议您查看database_cleaner。它自动处理ID重置。你可以告诉它只删除这样的选定表:

DatabaseCleaner.clean_with(:truncation, :only => %w[clients employees])

答案 2 :(得分:5)

我假设您不关心数据:

def self.truncate!
  connection.execute("truncate table #{quoted_table_name}")
end

或者如果你这样做,但不是太多(有一段时间数据只存在于内存中):

def self.truncate_preserving_data!
  data = all.map(&:clone).each{|r| raise "Record would not be able to be saved" unless r.valid? }
  connection.execute("truncate table #{quoted_table_name}")
  data.each(&:save)
end

这将给出具有相同属性的新记录,但id从1开始。

此表的任何belongs_to都可能变得棘手。

答案 3 :(得分:3)

基于@hgmnz的答案,我制作了这个方法,将序列设置为您喜欢的任何值...(仅使用Postgres适配器进行测试。)

# change the database sequence to force the next record to have a given id
def set_next_id table_name, next_id
  connection = ActiveRecord::Base.connection
  def connection.set_next_id table, next_id
    pk, sequence = pk_and_sequence_for(table)
    quoted_sequence = quote_table_name(sequence)
    select_value <<-end_sql, 'SCHEMA'
      SELECT setval('#{quoted_sequence}', #{next_id}, false)
    end_sql
  end
  connection.set_next_id(table_name, next_id)
end

答案 4 :(得分:1)

一个问题是,对于不同的数据库序列,自动增量等,这些字段的实现方式不同。

您可以随时删除并重新添加表格。

答案 5 :(得分:1)

Rails中没有这样的东西。如果您需要一个漂亮的ID来显示用户,那么将它们存储在一个单独的表中并重复使用它们。

答案 6 :(得分:1)

如果由rails设置了_ids,则只能在rails 中执行 。只要数据库设置了_ids,就不能在不使用SQL的情况下控制它们。

旁注:我想使用rails来定期调用重置或删除的SQL过程并重新创建序列不会是纯粹的SQL解决方案,但我不认为这就是你所要求的...... < / p>

编辑:

免责声明:我对rails不太了解。

从SQL的角度来看,如果你有一个包含id first_name last_name列的表,而你通常insert into table (first_name, last_name) values ('bob', 'smith'),你可以将查询更改为insert into table (id, first_name, last_name) values ([variable set by rails], 'bob', 'smith')这样,_id由变量设置而不是由SQL自动设置。此时,rails可以完全控制_ids的内容(尽管如果它是PK,你需要确保在它仍在那里时不要使用相同的值)。

如果要将作业保留到数据库中,则必须运行rails(在任何时间表上):

DROP SEQUENCE MY_SEQ;
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1;

以任何顺序控制表的ID。这将摆脱当前序列,并创建一个新序列。这是我知道你“重置”序列的最简单方法。

答案 7 :(得分:0)

Rails方式,例如MySQL,但丢失了表users 中的所有数据:

ActiveRecord::Base.connection.execute('TRUNCATE TABLE users;')

也许可以帮助别人;)

答案 8 :(得分:0)

有CounterCache方法:
https://www.rubydoc.info/docs/rails/4.1.7/ActiveRecord/CounterCache/ClassMethods
我使用了times(2),它似乎可以正常工作。