Wie mache ich eine Spalte eindeutig und indiziere sie in einer Ruby on Rails-Migration?

416

Ich möchte eine Spalte uniquein Ruby on Rails-Migrationsskript erstellen. Was ist der beste Weg, um es zu tun? Gibt es auch eine Möglichkeit, eine Spalte in einer Tabelle zu indizieren?

Ich möchte uniqueSpalten in einer Datenbank erzwingen, anstatt sie nur zu verwenden :validate_uniqueness_of.

Tam
quelle

Antworten:

685

Die kurze Antwort für alte Versionen von Rails (siehe andere Antworten für Rails 4+):

add_index :table_name, :column_name, unique: true

Um mehrere Spalten zusammen zu indizieren, übergeben Sie ein Array von Spaltennamen anstelle eines einzelnen Spaltennamens.

add_index :table_name, [:column_name_a, :column_name_b], unique: true

Wenn Sie " name: "whatever"Indexname ... ist zu lang" erhalten, können Sie die Methode add_index hinzufügen , um den Namen zu verkürzen.

Für eine fein abgestimmte Steuerung gibt es eine " execute" -Methode, die direktes SQL ausführt.

Das ist es!

Wenn Sie dies als Ersatz für reguläre Validierungen alter Modelle tun, überprüfen Sie, wie dies funktioniert. Die Fehlerberichterstattung an den Benutzer ist ohne Validierungen auf Modellebene wahrscheinlich nicht so gut. Sie können immer beides tun.

ndp
quelle
36
+1 für den Vorschlag, die validates_uniqueness_of weiterhin zu verwenden. Die Fehlerbehandlung ist viel sauberer, wenn diese Methode für die Kosten einer einzelnen indizierten Abfrage verwendet wird. Ich würde vorschlagen, dass er beides tut
Steve Weet
1
Ich habe versucht, dass es nicht zu funktionieren scheint! Ich könnte zwei Datensätze mit dem Spaltennamen einfügen, den ich als eindeutig definiert habe! Ich verwende Rails 2.3.4 und MySql. Irgendwelche Ideen?
Tam
Ich habe Ihren zweiten Vorschlag mit execute verwendet: Führen Sie "ALTER TABLE users ADD UNIQUE (email)" aus, und es funktioniert! Ich bin mir nicht sicher, warum der erste nicht daran interessiert sein würde zu wissen
Tam
1
Wenn indexed columns are not uniquebeim Versuch, einen eindeutigen Index zu erstellen , eine Fehlermeldung angezeigt wird, liegt dies möglicherweise daran, dass die Daten in der Tabelle bereits Duplikate enthalten. Versuchen Sie, die doppelten Daten zu entfernen und die Migration erneut auszuführen.
Hartley Brody
5
Wenn Sie "Indexname ... ist zu lang" erhalten, können Sie , :name => "whatever"die add_indexMethode ergänzen , um den Namen zu verkürzen.
Rick Smith
128

Rails generieren Migration add_index_to_table_name column_name: uniq

oder

Rails generieren Migration add_column_name_to_table_name column_name: string: uniq: index

erzeugt

class AddIndexToModerators < ActiveRecord::Migration
  def change
    add_column :moderators, :username, :string
    add_index :moderators, :username, unique: true
  end
end

Wenn Sie einer vorhandenen Spalte einen Index hinzufügen, entfernen oder kommentieren Sie die add_columnZeile oder setzen Sie ein Häkchen

add_column :moderators, :username, :string unless column_exists? :moderators, :username
d.danailov
quelle
5
Ich habe dies positiv bewertet, weil ich das Befehlszeilenformular haben wollte. Aber es ist albern, dass es die Spalte hinzufügt, selbst wenn ich es spezifiziere add_index...und nicht add_column....
Tyler Collier
1
Ja, vielleicht in der nächsten Version.
d.danailov
49

Da dies noch nicht erwähnt wurde, aber die Frage beantwortet, die ich hatte, als ich diese Seite gefunden habe, können Sie auch angeben, dass ein Index eindeutig sein soll, wenn Sie ihn über t.referencesoder hinzufügen t.belongs_to:

create_table :accounts do |t|
  t.references :user, index: { unique: true } # or t.belongs_to

  # other columns...
end

(ab mindestens Rails 4.2.7)

Steve Grossi
quelle
42

Wenn Sie eine neue Tabelle erstellen, können Sie die Inline-Verknüpfung verwenden:

  def change
    create_table :posts do |t|
      t.string :title, null: false, index: { unique: true }
      t.timestamps
    end
  end
Pioz
quelle
14

Ich verwende Rails 5 und die obigen Antworten funktionieren hervorragend. Hier ist ein anderer Weg, der auch für mich funktioniert hat (der Tabellenname ist :peopleund der Spaltenname ist :email_address)

class AddIndexToEmailAddress < ActiveRecord::Migration[5.0]
  def change
    change_table :people do |t|
      t.index :email_address, unique: true
    end
  end
end
Nicholas Nelson
quelle
1

Möglicherweise möchten Sie einen Namen für den eindeutigen Schlüssel hinzufügen, da der Standardname für den eindeutigen Schlüssel per Schiene zu lang sein kann, für den die Datenbank den Fehler auslösen kann.

Um einen Namen für Ihren Index hinzuzufügen, verwenden Sie einfach die name:Option. Die Migrationsabfrage könnte ungefähr so ​​aussehen:

add_index :table_name, [:column_name_a, :column_name_b, ... :column_name_n], unique: true, name: 'my_custom_index_name'

Weitere Informationen - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index

Swaps
quelle
1
add_index :table_name, :column_name, unique: true

Um mehrere Spalten zusammen zu indizieren, übergeben Sie ein Array von Spaltennamen anstelle eines einzelnen Spaltennamens.

murali
quelle
0

Wenn Sie die DB-Spalte nicht eindeutig hinzugefügt haben, fügen Sie diese Validierung einfach im Modell hinzu, um zu überprüfen, ob das Feld eindeutig ist:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

siehe hier oben dient nur zu Testzwecken. Fügen Sie den Index hinzu, indem Sie die DB-Spalte wie von @Nate vorgeschlagen ändern

Weitere Informationen finden Sie im Index

Ravistm
quelle
2
Ich würde nicht empfehlen, nur die Validierung ohne entsprechenden Index hinzuzufügen. Die bessere Option besteht darin, vorhandene Duplikate zu bereinigen und dann den Index hinzuzufügen. Andernfalls besteht die Gefahr, dass vorhandene Daten ungültig werden (was dazu führt, dass Aktualisierungen dieser Zeilen fehlschlagen), und es kann dennoch zu Duplikaten kommen, wenn Sie Code haben, der die Rails-Validierungen überspringt. (zB beim Ausführen eines update_all oder direkter SQL-Einfügungen)
Nate