web-dev-qa-db-ja.com

Rails idフィールドのシードをリセットする方法

この質問に対する「純粋なSQL」の回答を見つけました。 in Railsで特定のテーブルのIDフィールドをリセットする方法はありますか?
なぜこれをしたいのですか?データが常に移動するテーブルがあるので、100行を超えることはめったにありませんが、常に異なります。現在は25,000までですが、それだけでは意味がありません。 Rails app(rufus-scheduler))の内部にあるスケジューラを使用して、idフィールドのリセットを毎月実行するつもりです。

42
Trevoke

私はhgimenezの答えと これもう1つ に基づいた解決策を思い付きました。

私は通常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])
56
kikito

使用しているDBMSについては言及していません。これがpostgreSQLの場合、ActiveRecord postgresアダプタにはreset_pk_sequences!使用できるメソッド:

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

あなたはデータを気にしないと思います:

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_toingこのテーブルを使用すると、不自然になる可能性があります。

5
cwninja

@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
3
AlexChaffee

1つの問題は、これらの種類のフィールドが、データベース、シーケンス、自動インクリメントなどによって異なる方法で実装されることです。

テーブルはいつでも削除して再度追加できます。

1
MattMcKnight

Railsにはそのようなものはありません。ユーザーを表示するためのNice IDが必要な場合は、それらを別のテーブルに格納して再利用します。

1
Jonas Elfström

in Rails を実行できるのは、_idがRailsによって設定されている場合のみです。 _idがデータベースによって設定されている限り、SQLを使用せずにそれらを制御することはできません。

補足:Railsを使用して、シーケンスをリセットまたはドロップして再作成するSQLプロシージャを定期的に呼び出すことは、純粋なSQLソリューションではないと思いますが、それはあなたが考えていることではないと思いますもう一度尋ねます...

編集:

免責事項: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は_idが何であるかを完全に制御します(ただし、それがPKの場合、まだそこにいる間は同じ値を使用しないようにする必要があります)。

割り当てをデータベースに任せる場合は、Rails)を(タイムスケジュールに関係なく)実行する必要があります。

_DROP SEQUENCE MY_SEQ;
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1;
_

テーブルのIDを制御するシーケンスに。これにより、現在のシーケンスが削除され、新しいシーケンスが作成されます。これは、あなたがシーケンスを「リセット」したことを知る最も簡単な方法です。

1
David Oneill

Rails way for e.g. MySQL、ただしテーブルusersのすべてのデータが失われました

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

多分誰かを助ける;)

0
Štefan Bartoš

CounterCacheメソッドがあります。
https://www.rubydoc.info/docs/Rails/4.1.7/ActiveRecord/CounterCache/ClassMethods
私はArticle.reset_counters Article.all.length - 1と動作するように見えました。

0
tbushman