Migracja w celu dodania unikatowego ograniczenia do kombinacji kolumn


140

Potrzebuję migracji, aby zastosować unikalne ograniczenie do kombinacji kolumn. czyli na peoplestole, kombinacji first_name, last_Namei Dobpowinien być unikalny.

Odpowiedzi:


243

add_index :people, [:firstname, :lastname, :dob], :unique => true


12
Myślę, że to dodanie unikalnego indeksu, a nie ograniczenia. A może indeks dodaje również ograniczenie?
Paul Cantrell

16
Nie, wszystko jest w porządku. Mój błąd! Unikalne ograniczenie pochodzi z unikalnego indeksu.
Paul Cantrell

7
Zgadzam się z @ paul-cantrell: czy nie ma sposobu, aby dodać tylko ograniczenie, a nie indeks (który ma wpływ na przechowywanie db)
Augustin Riedinger

17
Problem z walidacją na poziomie modelu polega na tym, że nie jest skalowany. Dwa serwery mogą uruchamiać te same dane w tym samym czasie (jak podwójne dotknięcie aplikacji z ciężkim interfejsem API). Mam teraz dwa identyczne rekordy w mojej bazie danych, a model ma walidację ...
baash05

6
Lubię mieć jedno i drugie ... Dla pewności
baash05

25

Według howmanyofme.com w samych Stanach Zjednoczonych 46.427 osób nazywa się John Smith. To około 127 lat dni. Ponieważ jest to znacznie więcej niż średnia długość życia człowieka, oznacza to, że zderzenie DOB jest matematycznie pewne.

Mówię tylko, że ta szczególna kombinacja unikalnych pól może w przyszłości doprowadzić do skrajnej frustracji użytkownika / klienta.

W razie potrzeby weź pod uwagę coś, co jest rzeczywiście unikalne, na przykład krajowy numer identyfikacyjny.

(Zdaję sobie sprawę, że jestem bardzo spóźniony na przyjęcie z tym, ale może to pomóc przyszłym czytelnikom.)


3
hrm ... z pewnością masz rację. ale prawdopodobnie był to tylko przykład tego, co chciał zrobić Ian, żeby wyjaśnić pytanie.
eritiro

1
Może. Jednak odpowiedź nie była przeznaczona dla Iana. Albo rzeczywiście Rangalo.
A Fader Darkly

Był przeznaczony dla wszystkich foo-ów, nie tylko dla Iana czy rangalo.
ARK

21

Możesz chcieć dodać ograniczenie bez indeksu. Zależy to od używanej bazy danych. Poniżej znajduje się przykładowy kod migracji dla Postgres. (tracking_number, carrier)to lista kolumn, których chcesz użyć dla ograniczenia.

class AddUniqeConstraintToShipments < ActiveRecord::Migration
  def up
    execute <<-SQL
      alter table shipments
        add constraint shipment_tracking_number unique (tracking_number, carrier);
    SQL
  end

  def down
    execute <<-SQL
      alter table shipments
        drop constraint if exists shipment_tracking_number;
    SQL
  end
end

Możesz dodać różne ograniczenia. Przeczytaj dokumentację


12
Dokumentacja PostgreSQL 9.4 mówi: Dodanie unikalnego ograniczenia automatycznie utworzy unikalny indeks btree w kolumnie lub grupie kolumn użytych w ograniczeniu. Ograniczenie unikalności tylko dla niektórych wierszy można wymusić, tworząc indeks częściowy. Więc IMHO nie ma potrzeby przechodzenia do surowego SQL, gdy wynik będzie w zasadzie taki sam, jak przy użyciu add_indexmetody. ;)
Rafał Cieślak

8
Właściwie jest jeden powód: jest to szczegół implementacyjny i dokumentacja zniechęca . Pamiętaj również, że nie możesz odwoływać się do ograniczenia za pomocą nazwy, ponieważ nie jest ono dodawane do pg_constrainttabeli.
kaikuchn

8

Cześć Możesz na przykład dodać unikalny indeks w migracji do kolumn

add_index(:accounts, [:branch_id, :party_id], :unique => true)

lub oddzielne unikalne indeksy dla każdej kolumny


Przepraszam, zadziałało, najpierw próbowałem przez edycję i istniejącą migrację, która nie działała, potem dodałem nową i zadziałało, dzięki.
rangalo

4

W typowym przykładzie tabeli łączenia między użytkownikami i postami:

create_table :users
create_table :posts

create_table :ownerships do |t|
  t.belongs_to :user, foreign_key: true, null: false
  t.belongs_to :post, foreign_key: true, null: false
end

add_index :ownerships, [:user_id, :post_id], unique: true

Próba utworzenia dwóch podobnych rekordów spowoduje błąd bazy danych (w moim przypadku Postgres):

ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_ownerships_on_user_id_and_post_id"
DETAIL:  Key (user_id, post_id)=(1, 1) already exists.
: INSERT INTO "ownerships" ("user_id", "post_id") VALUES ($1, $2) RETURNING "id"

np. robiąc to:

Ownership.create!(user_id: user_id, post_id: post_id)
Ownership.create!(user_id: user_id, post_id: post_id)

W pełni działający przykład: https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

db/schema.rbwygenerowane: https://gist.github.com/Dorian/a8449287fa62b88463f48da986c1744a


4

Ze względu na kompletność i dla uniknięcia nieporozumień, są trzy sposoby zrobienia tego samego:
Dodanie nazwanego, unikalnego ograniczenia do kombinacji kolumn w Railsach 5.2+

Załóżmy, że mamy tabelę Lokalizacje, która należy do reklamodawcy i zawiera kolumnę kod_referencyjny, a Ty chcesz mieć tylko jeden kod referencyjny na reklamodawcę. więc chcesz dodać unikatowe ograniczenie do kombinacji kolumn i nazwać je.

Robić:

rails g migration AddUniquenessConstraintToLocations

I spraw, aby Twoja migracja wyglądała jak ta jedna linijka:

class AddUniquenessConstraintToLocations < ActiveRecord::Migration[5.2]
  def change
    add_index :locations, [:reference_code, :advertiser_id], unique: true, name: 'uniq_reference_code_per_advertiser'
  end
end

LUB ta wersja bloku.

class AddUniquenessConstraintToLocations < ActiveRecord::Migration[5.2]
  def change
    change_table :locations do |t|
     t.index ['reference_code', 'advertiser_id'], name: 'uniq_reference_code_per_advertiser', unique: true
    end
  end
end

LUB ta surowa wersja SQL

class AddUniquenessConstraintToLocations < ActiveRecord::Migration[5.2]
  def change
      execute <<-SQL
          ALTER TABLE locations
            ADD CONSTRAINT uniq_reference_code_per_advertiser UNIQUE (reference_code, advertiser_id);
        SQL
  end
end

Każde z nich będzie miało ten sam wynik, sprawdź schema.rb

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.