Schienen Sie bereits vorhandene ID automatisch zu

92

Ich erstelle einen neuen Rekord wie folgt:

truck = Truck.create(:name=>name, :user_id=>2)

Meine Datenbank enthält derzeit mehrere tausend Entitäten für LKWs, aber ich habe die IDs mehreren von ihnen so zugewiesen, dass einige IDs verfügbar waren. Was also passiert, ist, dass Rails ein Element mit der ID = 150 erstellt und es funktioniert einwandfrei. Aber dann wird versucht, ein Element zu erstellen und es zuzuweisen. ID = 151, aber diese ID ist möglicherweise bereits vorhanden. Daher wird der folgende Fehler angezeigt:

ActiveRecord::RecordNotUnique (PG::Error: ERROR: duplicate key value violates unique constraint "companies_pkey" DETAIL: Key (id)=(151) already exists.

Und wenn ich das nächste Mal die Aktion ausführe, wird einfach die ID 152 zugewiesen, die einwandfrei funktioniert, wenn dieser Wert noch nicht vergeben ist. Wie kann ich Rails veranlassen, zu überprüfen, ob eine ID bereits vorhanden ist, bevor sie zugewiesen wird?

Vielen Dank!

BEARBEITEN

Die LKW-ID wird dupliziert. Der Benutzer existiert bereits und ist in diesem Fall eine Konstante. Es ist tatsächlich ein altes Problem, mit dem ich mich befassen muss. Eine Möglichkeit besteht darin, die Tabelle bei let Rails neu zu erstellen und diesmal jede ID automatisch zuzuweisen. Ich fange an zu denken, dass dies die beste Wahl ist, weil ich einige andere Probleme habe, aber die Migration dafür wäre sehr kompliziert, da Truck in so vielen anderen Tabellen ein Fremdschlüssel ist. Gibt es eine einfache Möglichkeit, Rails eine neue Tabelle mit denselben Daten erstellen zu lassen, die bereits unter Truck gespeichert sind, mit automatisch zugewiesenen IDs und unter Beibehaltung aller vorhandenen Beziehungen?

D-Nizza
quelle
Warum lassen Sie Rails die ID nicht automatisch zuweisen? Das würde die Gefahr von Duplikaten ausschließen - oder handelt es sich um ein Altdatenproblem, bei dem Sie alte IDs aufbewahren müssen? Ich möchte nur den Business Case ein wenig verstehen, da dies beim Erstellen eines neuen Objekts nicht wie gewohnt ist.
MBHNYC
@MBHNYC Ich denke, dass D-Nice beim Erstellen des Unternehmens eine Benutzer-ID zuweist und keine ID, wie Sie denken (und ich auch für einen Moment).
Anil
Ooo guter Fang Anil - du hast vollkommen recht. @ D-Nice, füge vielleicht deine Migration für diese Tabelle zu deinem Beitrag hinzu, falls es etwas Seltsames gibt? Versuchung, diese user_id zu bearbeiten, um die Verwirrung zu beseitigen ..
MBHNYC
Nein, es war leider unklar, die Firmen-ID wird dupliziert. Der Benutzer existiert bereits und ist in diesem Fall eine Konstante. Es ist tatsächlich ein altes Problem, mit dem ich mich befassen muss. Wird Beitrag mit mehr Infos bearbeiten
D-Nice
Sie müssen die Tabelle nicht neu erstellen. Sehen Sie sich einfach meine Antwort an, um die Sequenz zurückzusetzen.
Dondi Michael Stroma

Antworten:

87

Rails verwendet wahrscheinlich die integrierte PostgreSQL-Sequenz. Die Idee einer Sequenz ist, dass sie nur einmal verwendet wird.

Die einfachste Lösung besteht darin, die Reihenfolge für die Spalte "company.id" mit einer Abfrage wie der folgenden auf den höchsten Wert in der Tabelle festzulegen:

SELECT setval('company_id_seq', (SELECT max(id) FROM company));

Ich vermute Ihren Sequenznamen "company_id_seq", Tabellennamen "company" und Spaltennamen "id" ... bitte ersetzen Sie sie durch die richtigen. Sie können den Sequenznamen mit SELECT pg_get_serial_sequence('tablename', 'columname');abrufen oder die Tabellendefinition mit anzeigen \d tablename.

Eine alternative Lösung besteht darin, die save () -Methode in Ihrer Unternehmensklasse zu überschreiben, um die Unternehmens-ID für neue Zeilen vor dem Speichern manuell festzulegen.

Dondi Michael Stroma
quelle
Ich vermute, das würde dazu führen, dass die automatische Zuweisung mit dem derzeit höchsten Wert + 1 beginnt.
D-Nice
Ich denke, dies ist die beste Antwort auf meine Frage, aber aus nicht verwandten Gründen muss ich einen Weg finden, die Strategie zu verwenden, die ich in meiner OP-Bearbeitung beschrieben habe
D-Nice
Ich verstehe nicht, warum das so war? Das ist mir passiert und ich würde gerne verstehen, wie das überhaupt möglich ist.
Hunt Burdick
2
@Websitescenes Wenn eine serielle Spalte in PostgreSQL vorhanden ist (eine serielle Spalte ist eine Spalte, in der der Standardwert der nächste Wert in einer Sequenz ist), wird die Sequenz nicht automatisch aktualisiert, wenn die Tabelle mit harten Werten in dieser Spalte gefüllt wird. Beispiel: create table t (id serial not null primary key); insert into t values (1); insert into t values (default); ERROR: duplicate key value violates unique constraint "t_pkey" DETAIL: Key (id)=(1) already exists.
Dondi Michael Stroma
204

Ich habe dies getan, was das Problem für mich gelöst hat.

ActiveRecord::Base.connection.tables.each do |t|
  ActiveRecord::Base.connection.reset_pk_sequence!(t)
end

Ich habe die reset_pk_sequence gefunden! von diesem Thread. http://www.ruby-forum.com/topic/64428

Ein Kuchen
quelle
4
Danke, die beste Lösung. Nach der Datenbankübertragung hatte ich das gleiche Problem.
Oleg Pasko
61
Oder das Einzeiler-Äquivalent (zum Kopieren / Einfügen von Schienenkonsolen):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
Irgendeine Idee, wie dies nicht mehr synchron ist?
Tall Paul
25

Basierend auf @Apie Antwort .

Sie können eine Aufgabe erstellen und bei Bedarf ausführen mit:

rake database:correction_seq_id

Sie erstellen Aufgaben wie folgt:

rails g task database correction_seq_id

Und in die generierte Datei ( lib/tasks/database.rake) setzen:

namespace :database do
    desc "Correction of sequences id"
    task correction_seq_id: :environment do
        ActiveRecord::Base.connection.tables.each do |t|
            ActiveRecord::Base.connection.reset_pk_sequence!(t)
        end
    end
end
inye
quelle
4

Klingt für mich nach einem Datenbankproblem und nicht nach einem Rails-Problem. Ist es möglich, dass Ihre Datenbank einen falschen Identitätsstart in Ihrer idSpalte enthält? Versuchen Sie zum Testen, einige Einfügungen direkt in Ihre Datenbank vorzunehmen, und prüfen Sie, ob dasselbe Verhalten vorliegt.

mynameiscoffey
quelle
3
Warum das Downvote? Dies ist das genaue Verhalten, das auftritt, wenn Sie Ihre Inkrementierungssequenz auf einen Wert einstellen, der niedriger als andere vorhandene Werte ist und daher beim Einfügen von Daten gelegentlich auf Kollisionen stößt. Das Plakat sagte bereits, dass es vorhandene Daten gibt, die in diesen Fall fallen.
mynameiscoffey
Ich kann gut einfügen. Nachdem ich diesen Fehler erhalten habe, kann ich dieselbe Aktion erneut ausführen und sie funktionieren lassen, wenn die nächste ID in der Sequenz noch nicht vergeben ist.
D-Nice
Anscheinend war dies meine Situation - ich bin auf das Problem gestoßen, aber der nächste Datensatz, den ich eingefügt habe, hat gut funktioniert, also muss er den Samen an die richtige Stelle gebracht haben.
Ben Wheeler
3

Ich habe dieses Problem mit dem folgenden Befehl behoben.

Führen Sie dies in Ihrer Rails-Konsole aus

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Jigar Bhatt
quelle