Une migration pour ajouter une contrainte unique à une combinaison de colonnes

140

Ce dont j'ai besoin est une migration pour appliquer une contrainte unique à une combinaison de colonnes. c'est-à-dire pour une peopletable, une combinaison de first_name, last_Nameet Dobdoit être unique.

Rangalo
la source

Réponses:

244

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

Robert Speicher
la source
12
Je pense que cela ajoute un index unique, pas une contrainte. Ou est-ce que l'index ajoute également la contrainte?
Paul Cantrell
17
Non, tout va bien. Ma faute! La contrainte unique vient avec l'index unique.
Paul Cantrell
7
Je suis d'accord avec @ paul-cantrell: n'y a-t-il aucun moyen d'ajouter uniquement une contrainte, pas un index (qui a des influences de stockage de base de données)
Augustin Riedinger
17
Le problème avec la validation au niveau du modèle est qu'elle ne s'adapte pas. Deux serveurs pourraient exécuter les mêmes données en même temps (comme un double tap sur une application lourde api) J'ai deux enregistrements identiques dans ma base de données en ce moment et le modèle a la validation ..
baash05
6
J'aime avoir les deux .. Juste pour être sûr
baash05
25

Selon howmanyofme.com, "Il y a 46 427 personnes nommées John Smith" rien qu'aux États-Unis. Cela fait environ 127 ans de jours. Comme c'est bien au-dessus de la durée de vie moyenne d'un être humain, cela signifie qu'un conflit DOB est mathématiquement certain.

Tout ce que je dis, c'est que cette combinaison particulière de champs uniques pourrait conduire à une frustration extrême des utilisateurs / clients à l'avenir.

Considérez quelque chose qui est en fait unique, comme un numéro d'identification national, le cas échéant.

(Je me rends compte que je suis très en retard à la fête avec celui-ci, mais cela pourrait aider les futurs lecteurs.)

Un fader sombre
la source
3
hrm ... vous avez certainement raison. mais c'était probablement juste un exemple de ce que Ian voulait faire juste pour clarifier la question.
eritiro
1
Peut être. La réponse n'était cependant pas destinée à Ian. Ou bien Rangalo.
A Fader Darkly
Il était destiné à tous les foo-s, pas seulement à Ian ou rangalo.
ARK
21

Vous souhaiterez peut-être ajouter une contrainte sans index. Cela dépendra de la base de données que vous utilisez. Voici un exemple de code de migration pour Postgres. (tracking_number, carrier)est une liste des colonnes que vous souhaitez utiliser pour la contrainte.

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

Vous pouvez ajouter différentes contraintes. Lire la documentation

Josh
la source
12
Docs for PostgreSQL 9.4 dit: L'ajout d'une contrainte unique créera automatiquement un index btree unique sur la colonne ou le groupe de colonnes utilisé dans la contrainte. Une contrainte d'unicité sur seulement certaines lignes peut être appliquée en créant un index partiel. Donc, à mon humble avis, il n'est pas nécessaire de passer au SQL brut lorsque le résultat sera fondamentalement le même que l'utilisation de la add_indexméthode. ;)
Rafał Cieślak
8
En fait, il y a une raison: c'est un détail de mise en œuvre et découragé par la documentation . Notez également que vous ne pouvez pas faire référence à la contrainte par son nom, car elle n'est pas ajoutée à la pg_constrainttable.
kaikuchn
8

Salut Vous pouvez ajouter un index unique dans votre migration aux colonnes par exemple

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

ou des index uniques séparés pour chaque colonne

Bohdan
la source
Désolé, cela a fonctionné, j'ai d'abord essayé l'édition et la migration existante qui n'a pas fonctionné, puis en ai ajouté une nouvelle et cela a fonctionné, merci.
rangalo
4

Dans l'exemple typique d'une table de jointure entre les utilisateurs et les publications:

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

Essayer de créer deux enregistrements similaires générera une erreur de base de données (Postgres dans mon cas):

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"

par exemple faire ça:

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

Exemple entièrement exécutable: https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

db/schema.rbgénéré: https://gist.github.com/Dorian/a8449287fa62b88463f48da986c1744a

Dorian
la source
4

Par souci d'exhaustivité et pour éviter toute confusion, voici 3 façons de faire la même chose:
Ajouter une contrainte unique nommée à une combinaison de colonnes dans Rails 5.2+

Supposons que nous ayons une table Emplacements qui appartient à un annonceur et que la colonne référence_code et que vous ne souhaitiez qu'un seul code de référence par annonceur. vous voulez donc ajouter une contrainte unique à une combinaison de colonnes et la nommer.

Faire:

rails g migration AddUniquenessConstraintToLocations

Et donnez à votre migration un aspect similaire à celui-ci:

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

OU cette version de bloc.

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

OU cette version SQL brute

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

Chacun de ceux-ci aura le même résultat, vérifiez votre schema.rb

Khalil Gharbaoui
la source