Eine Migration, um einer Kombination von Spalten eine eindeutige Einschränkung hinzuzufügen

140

Was ich brauche, ist eine Migration, um eine eindeutige Einschränkung auf eine Kombination von Spalten anzuwenden. dh für eine peopleTabelle, eine Kombination aus first_name, last_Nameund Dobsollte eindeutig sein.

Rangalo
quelle

Antworten:

242

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

Robert Speicher
quelle
12
Ich denke, das fügt einen eindeutigen Index hinzu, keine Einschränkung. Oder fügt der Index auch die Einschränkung hinzu?
Paul Cantrell
16
Nein, es ist alles gut. Mein Fehler! Die eindeutige Einschränkung wird mit dem eindeutigen Index geliefert.
Paul Cantrell
7
Ich stimme @ paul-cantrell zu: Es gibt keine Möglichkeit, nur eine Einschränkung hinzuzufügen, keinen Index (der DB-Speichereinflüsse hat)
Augustin Riedinger
17
Das Problem bei der Validierung auf Modellebene besteht darin, dass sie nicht skaliert. Zwei Server könnten die gleichen Daten gleichzeitig durchlaufen (wie ein doppeltes Tippen auf eine API-schwere App). Ich habe gerade zwei identische Datensätze in meiner
Datenbank
6
Ich möchte beide haben .. Nur um sicher zu gehen
baash05
25

Laut howmanyofme.com gibt es allein in den USA "46.427 Menschen namens John Smith". Das sind ungefähr 127 Jahre. Da dies weit über der durchschnittlichen Lebenserwartung eines Menschen liegt, bedeutet dies, dass ein DOB-Konflikt mathematisch sicher ist.

Ich sage nur, dass diese bestimmte Kombination einzigartiger Felder in Zukunft zu extremer Frustration zwischen Benutzern und Kunden führen kann.

Betrachten Sie etwas, das tatsächlich einzigartig ist, wie gegebenenfalls eine nationale Identifikationsnummer.

(Mir ist klar, dass ich mit diesem sehr spät zur Party komme, aber es könnte zukünftigen Lesern helfen.)

Ein Fader dunkel
quelle
3
hm ... du hast sicher recht. aber wahrscheinlich war es nur ein Beispiel dafür, was Ian tun wollte, um die Frage klar zu machen.
Eritiro
1
Vielleicht. Die Antwort war jedoch nicht für Ian gedacht. Oder Rangalo.
Ein Fader dunkel
Es war für alle Foo-s gedacht, nicht nur für Ian oder Rangalo.
ARK
20

Möglicherweise möchten Sie eine Einschränkung ohne Index hinzufügen. Dies hängt davon ab, welche Datenbank Sie verwenden. Unten finden Sie einen Beispiel für einen Migrationscode für Postgres. (tracking_number, carrier)ist eine Liste der Spalten, die Sie für die Einschränkung verwenden möchten.

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

Es gibt verschiedene Einschränkungen, die Sie hinzufügen können. Lesen Sie die Dokumente

Josh
quelle
12
In den Dokumenten für PostgreSQL 9.4 heißt es: Durch Hinzufügen einer eindeutigen Einschränkung wird automatisch ein eindeutiger btree-Index für die Spalte oder Gruppe von Spalten erstellt, die in der Einschränkung verwendet werden. Eine Eindeutigkeitsbeschränkung für nur einige Zeilen kann durch Erstellen eines Teilindex erzwungen werden. IMHO besteht also keine Notwendigkeit, auf Raw SQL zu verzichten, wenn das Ergebnis im Grunde das gleiche ist wie bei Verwendung der add_indexMethode. ;)
Rafał Cieślak
8
Tatsächlich gibt es einen Grund: Es handelt sich um ein Implementierungsdetail, das von den Dokumenten nicht empfohlen wird . Beachten Sie auch, dass Sie die Einschränkung nicht nach Namen referenzieren können, da sie nicht zur pg_constraintTabelle hinzugefügt wird .
Kaikuchn
8

Hallo, Sie können den Spalten beispielsweise einen eindeutigen Index für Ihre Migration hinzufügen

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

oder separate eindeutige Indizes für jede Spalte

Bohdan
quelle
Entschuldigung, es hat funktioniert. Zuerst habe ich versucht, eine vorhandene Migration zu bearbeiten und zu bearbeiten, die dann nicht funktioniert hat. Dann habe ich eine neue hinzugefügt und es hat funktioniert, danke.
Rangalo
4

Im typischen Beispiel einer Join-Tabelle zwischen Benutzern und Posts:

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

Der Versuch, zwei ähnliche Datensätze zu erstellen, führt zu einem Datenbankfehler (in meinem Fall 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"

zB das tun:

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

Vollständig ausführbares Beispiel: https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

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

Dorian
quelle
4

Der Vollständigkeit halber und um Verwirrung zu vermeiden, gibt es drei Möglichkeiten, dasselbe zu tun:
Hinzufügen einer benannten eindeutigen Einschränkung zu einer Kombination von Spalten in Rails 5.2+

Angenommen, wir haben eine Standorttabelle, die zu einem Werbetreibenden gehört und die Spalte reference_code enthält, und Sie möchten nur 1 Referenzcode pro Werbetreibendem. Sie möchten also einer Kombination von Spalten eine eindeutige Einschränkung hinzufügen und sie benennen.

Machen:

rails g migration AddUniquenessConstraintToLocations

Und lassen Sie Ihre Migration entweder so aussehen wie diesen einen Liner:

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

ODER diese Blockversion.

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

ODER diese unformatierte SQL-Version

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

Alle diese haben das gleiche Ergebnis, überprüfen Sie Ihre schema.rb

Khalil Gharbaoui
quelle