web-dev-qa-db-fra.com

Manière Rails de réinitialiser les graines sur le champ id

J'ai trouvé le "SQL pur" réponses à cette question. Existe-t-il un moyen,dans Rails, de réinitialiser le champ id pour une table spécifique?
Pourquoi est-ce que je veux faire ça? Parce que j'ai des tables avec des données constamment en mouvement - rarement plus de 100 lignes, mais toujours différentes. Il est à 25k maintenant, et cela ne sert à rien. J'ai l'intention d'utiliser un planificateur interne à l'application Rails (rufus-scheduler) pour exécuter le champ id chaque mois.

40
Trevoke

Je suis sorti avec une solution basée sur la réponse de hgimenez et cet autre

Comme je travaille habituellement avec Sqlite ou PostgreSQL, je n’ai développé que pour ceux-ci; mais l'étendre à, disons, MySQL, ne devrait pas être trop gênant.

Mettez ceci à l'intérieur de lib/et en avez besoin sur un initialiseur:

# 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

Usage:

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

EDIT: Étant donné que le cas le plus habituel dans lequel vous voudrez faire cela est après avoir effacé une table de base de données, je vous recommande de regarder database_cleaner . Il gère l'ID réinitialisant automatiquement. Vous pouvez lui dire de supprimer uniquement les tables sélectionnées comme ceci:

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

Vous n'avez jamais mentionné le SGBD que vous utilisez. S'il s'agit de postgreSQL, l'adaptateur de postgres ActiveRecord a une méthode reset_pk_sequences! que vous pouvez utiliser:

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

Je suppose que vous ne vous souciez pas des données:

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

Ou si vous le faites, mais pas trop (il y a une tranche de temps où les données n'existent que dans la mémoire):

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

Cela donnera de nouveaux enregistrements, avec les mêmes attributs, mais les identifiants commencent à 1.

Tout ce que belongs_toing cette table pourrait devenir vicieux.

5
cwninja

Sur la base de la réponse de @hgmnz, j’ai conçu cette méthode qui définira la séquence sur la valeur de votre choix ... (uniquement testé avec l’adaptateur 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
2
AlexChaffee

Un problème est que ces types de champs sont implémentés différemment pour différentes bases de données: séquences, incréments automatiques, etc. 

Vous pouvez toujours supprimer et rajouter le tableau.

1
MattMcKnight

Non, il n'y a rien de tel dans Rails. Si vous avez besoin d'un identifiant Nice pour afficher les utilisateurs, stockez-les dans un tableau séparé et réutilisez-les.

1
Jonas Elfström

Vous ne pouvez le faire que dans Rails si les _ids sont définis par Rails. Tant que les _ids sont définis par votre base de données, vous ne pourrez pas les contrôler sans utiliser SQL. 

Note latérale: J'imagine qu'utiliser Rails pour appeler régulièrement une procédure SQL qui réinitialise ou supprime et recrée une séquence ne serait pas une solution purement SQL, mais je ne pense pas que ce soit ce que vous demandez ...

MODIFIER:

Disclaimer: Je ne sais pas grand chose de Rails.

Du point de vue SQL, si vous avez une table avec des colonnes id first_name last_name et que vous avez généralement insert into table (first_name, last_name) values ('bob', 'smith'), vous pouvez simplement modifier vos requêtes en insert into table (id, first_name, last_name) values ([variable set by Rails], 'bob', 'smith'). Ainsi, le _id est défini par une variable, au lieu d'être défini automatiquement par SQL. À ce stade, Rails contrôle entièrement ce que sont les _ids (bien que, dans le cas d'un PK, vous devez vous assurer que vous n'utilisez pas la même valeur tant qu'elle est encore présente). 

Si vous souhaitez laisser l’attribution à la base de données, vous devez exécuter Rails (quel que soit le calendrier choisi), par exemple: 

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

quelle que soit la séquence qui contrôle les identifiants de votre table. Cela éliminera la séquence en cours et en créera une nouvelle. C'est la façon la plus simple que je connaisse de «réinitialiser» une séquence.

1
David Oneill

Voie de chemin pour par exemple MySQL, mais avec toutes les données perdues dans la table users:

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

Peut-être aide quelqu'un;)

0
Štefan Bartoš

Il existe des méthodes CounterCache:
http://www.rubydoc.info/docs/Rails/4.1.7/ActiveRecord/CounterCache/ClassMethods
J'ai utilisé Article.reset_counters Article.all.length - 1 et cela a semblé fonctionner.

0
tbushman