Jak uczynić kolumnę unikalną i zindeksować ją w migracji Ruby on Rails?

416

Chciałbym utworzyć kolumnę uniquew skrypcie migracji Ruby on Rails. Jak najlepiej to zrobić? Czy istnieje również sposób na indeksowanie kolumny w tabeli?

Chciałbym wymusić uniquekolumny w bazie danych, a nie tylko ich używać :validate_uniqueness_of.

Tam
źródło

Odpowiedzi:

686

Krótka odpowiedź dla starych wersji Railsów (zobacz inne odpowiedzi dla Railsów 4+):

add_index :table_name, :column_name, unique: true

Aby zindeksować wiele kolumn razem, przekazujesz tablicę nazw kolumn zamiast nazwy pojedynczej kolumny,

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

Jeśli pojawi się „nazwa indeksu ... jest za długa”, możesz dodać name: "whatever"do metody add_index, aby skrócić nazwę.

Aby uzyskać precyzyjną kontrolę, istnieje executemetoda, która wykonuje prosty SQL.

Otóż ​​to!

Jeśli robisz to jako zamiennik regularnych sprawdzeń starego modelu, sprawdź, jak to działa. Zgłaszanie błędów do użytkownika prawdopodobnie nie będzie tak przyjemne bez sprawdzania poprawności na poziomie modelu. Zawsze możesz zrobić jedno i drugie.

ndp
źródło
36
+1 za sugerowanie dalszego korzystania z validates_uniqueness_of. Obsługa błędów jest znacznie bardziej przejrzysta przy użyciu tej metody w stosunku do pojedynczego zaindeksowanego zapytania Sugerowałbym, że robi to oba
Steve Weet
1
Próbowałem, że to nie działa! Mógłbym wstawić dwa rekordy z nazwą kolumny, którą zdefiniowałem jako unikalną! Korzystam z Rails 2.3.4 i MySql jakieś pomysły?
Tam
Skorzystałem z drugiej sugestii przy użyciu polecenia execute: wykonaj polecenie „ZMIEŃ TABELĘ użytkowników DODAJ UNIKALNE (e-mail)” i działa! nie jestem pewien, dlaczego pierwszy nie byłby zainteresowany poznaniem
Tam
1
Jeśli indexed columns are not uniquepodczas próby utworzenia unikalnego indeksu pojawi się błąd, przyczyną może być to, że dane w tabeli już zawierają duplikaty. Spróbuj usunąć zduplikowane dane i ponownie uruchom migrację.
Hartley Brody
5
Jeśli pojawi się „nazwa indeksu ... jest za długa”, możesz dodać , :name => "whatever"do add_indexmetody, aby skrócić nazwę.
Rick Smith
129

szyny generują migrację add_index_to_table_name nazwa_kolumny: uniq

lub

szyny generują migrację add_column_name_to_table_name nazwa_kolumny: string: uniq: index

generuje

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

Jeśli dodajesz indeks do istniejącej kolumny, usuń lub skomentuj add_columnlinię lub zaznacz

add_column :moderators, :username, :string unless column_exists? :moderators, :username
d.danailov
źródło
5
Głosowałem za tym, ponieważ chciałem formularza wiersza poleceń. Ale to głupie, że dodaje kolumnę nawet wtedy, gdy określam add_index...i nie add_column....
Tyler Collier
1
Tak, może w następnej wersji.
d.danailov
49

Ponieważ nie zostało to jeszcze wspomniane, ale odpowiada na pytanie, które miałem, kiedy znalazłem tę stronę, możesz również określić, że indeks powinien być unikalny podczas dodawania go za pośrednictwem t.referenceslub t.belongs_to:

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

  # other columns...
end

(od co najmniej Rails 4.2.7)

Steve Grossi
źródło
42

Jeśli tworzysz nową tabelę, możesz użyć wbudowanego skrótu:

  def change
    create_table :posts do |t|
      t.string :title, null: false, index: { unique: true }
      t.timestamps
    end
  end
Pioz
źródło
14

Używam Rails 5 i powyższe odpowiedzi działają świetnie; oto inny sposób, który również działał dla mnie (nazwa tabeli to :peoplei nazwa kolumny to :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
źródło
1

Możesz dodać nazwę unikalnego klucza tyle razy, ile razy domyślna nazwa unikalnego klucza przez szyny może być zbyt długa, przez co DB może wyrzucić błąd.

Aby dodać nazwę do indeksu, skorzystaj z name:opcji. Zapytanie migracyjne może wyglądać mniej więcej tak:

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

Więcej informacji - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index

Zamiana
źródło
1
add_index :table_name, :column_name, unique: true

Aby zindeksować wiele kolumn razem, przekazujesz tablicę nazw kolumn zamiast nazwy pojedynczej kolumny.

murali
źródło
0

Jeśli przegapiłeś dodanie unikatowego do kolumny DB, po prostu dodaj tę weryfikację w modelu, aby sprawdzić, czy pole jest unikalne:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

patrz tutaj Powyżej służy wyłącznie do celów testowych, dodaj indeks , zmieniając kolumnę DB zgodnie z sugestią @Nate

więcej informacji można znaleźć w indeksie

Ravistm
źródło
2
Nie polecałbym dodawania sprawdzania poprawności bez odpowiedniego indeksu. Lepszą opcją jest usunięcie wszelkich istniejących duplikatów, a następnie dodanie indeksu. W przeciwnym razie ryzykujesz unieważnieniem istniejących danych (co spowoduje niepowodzenie aktualizacji tych wierszy) i nadal możesz mieć duplikaty, jeśli masz kod pomijający sprawdzanie poprawności Railsów. (np. podczas uruchamiania update_all lub bezpośrednich wstawień SQL)
Nate