it-swarm.com.de

Rails Weg, um Samen auf ID-Feld zurückzusetzen

Ich habe die "pure SQL" Antworten auf diese Frage gefunden. Gibt es eine Möglichkeit, in Rails , um das ID-Feld für eine bestimmte Tabelle zurückzusetzen?
Warum will ich das machen? Denn ich habe Tabellen mit ständig bewegten Daten - selten mehr als 100 Zeilen, aber immer anders. Es ist jetzt bis zu 25.000, und das macht einfach keinen Sinn. Ich beabsichtige die Verwendung eines Schedulers innerhalb der Rails-App (Rufus-Scheduler), um das id-Feld monatlich neu zu starten.

40
Trevoke

Ich habe eine Lösung gefunden, die auf der Antwort von hgimenez und dieser anderen basiert. 

Da ich normalerweise mit Sqlite oder PostgreSQL arbeite, habe ich nur für diese entwickelt. Aber es auszudehnen, sagen wir MySQL, sollte nicht zu lästig sein.

Fügen Sie dies in lib/ein und fordern Sie es an einen Initialisierer:

# 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

Verwendungszweck:

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

EDIT: Da der üblichste Fall das Löschen einer Datenbanktabelle ist, empfehle ich, einen Blick auf database_cleaner zu werfen. Die ID wird automatisch zurückgesetzt. Sie können es anweisen, nur ausgewählte Tabellen wie folgt zu löschen:

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

Sie haben nie erwähnt, welches DBMS Sie verwenden. Wenn dies postgreSQL ist, verfügt der Postgres-Adapter von ActiveRecord über eine reset_pk_sequences!-Methode, die Sie verwenden könnten:

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

Ich nehme an, Sie interessieren sich nicht für die Daten:

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

Oder wenn Sie dies tun, aber nicht zu viel (es gibt eine Zeitspanne, in der die Daten nur im Speicher vorhanden sind):

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

Dies gibt neue Datensätze mit denselben Attributen, aber die ID beginnt bei 1.

Alles, was an dieser Tabelle belongs_to liegt, könnte schief werden.

5
cwninja

Basierend auf der Antwort von @hgmnz habe ich diese Methode erstellt, mit der die Reihenfolge auf einen beliebigen Wert gesetzt wird ... (Nur mit dem Postgres-Adapter getestet.)

# 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

Ein Problem ist, dass diese Arten von Feldern für unterschiedliche Datenbanken unterschiedlich implementiert werden - Sequenzen, Auto-Inkremente usw. 

Sie können die Tabelle jederzeit löschen und erneut hinzufügen.

1
MattMcKnight

Nein, so etwas gibt es bei Rails nicht. Wenn Sie eine Nice-ID benötigen, um die Benutzer anzuzeigen, speichern Sie sie in einer separaten Tabelle und verwenden Sie sie erneut.

1
Jonas Elfström

Sie können dies in Rails nur tun, wenn die _ids von Rails gesetzt werden. Solange die _ids von Ihrer Datenbank festgelegt werden, können Sie sie nicht ohne SQL steuern. 

Randbemerkung: Ich denke, dass das Verwenden von Rails zum regelmäßigen Aufrufen einer SQL-Prozedur, die eine Sequenz zurücksetzt oder löscht und eine Sequenz neu erstellt, keine reine SQL-Lösung wäre, aber ich glaube nicht, dass Sie das fragen ...

BEARBEITEN:

Haftungsausschluss: Ich weiß nicht viel über Rails.

Wenn Sie aus der SQL-Perspektive eine Tabelle mit Spalten id first_name last_name und normalerweise insert into table (first_name, last_name) values ('bob', 'smith') haben, können Sie Ihre Abfragen einfach in insert into table (id, first_name, last_name) values ([variable set by Rails], 'bob', 'smith') ändern. Auf diese Weise wird die _id durch eine Variable festgelegt, anstatt automatisch von SQL gesetzt zu werden. An diesem Punkt hat Rails die vollständige Kontrolle über die _ids-Werte (obwohl es sich bei einem PK um einen Server handelt, müssen Sie sicherstellen, dass Sie nicht denselben Wert verwenden, solange er noch vorhanden ist). 

Wenn Sie die Zuweisung der Datenbank überlassen, müssen Sie Rails (zu einem beliebigen Zeitplan) ausführen, z. 

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

in welcher Reihenfolge steuern Sie die IDs für Ihre Tabelle. Dadurch wird die aktuelle Sequenz gelöscht und eine neue erstellt. Dies ist der einfachste Weg, von dem ich weiß, dass Sie eine Sequenz zurücksetzen.

1
David Oneill

Schienenweg für z. MySQL, aber mit Verlust aller Daten in Tabelle users:

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

Vielleicht hilft jemandem;)

0
Štefan Bartoš

Es gibt CounterCache-Methoden:
https://www.rubydoc.info/docs/Rails/4.1.7/ActiveRecord/CounterCache/ClassMethods
Ich habe Article.reset_counters Article.all.length - 1 verwendet und es schien zu funktionieren.

0
tbushman