Uma migração para adicionar restrição exclusiva a uma combinação de colunas


140

O que eu preciso é de uma migração para aplicar restrições exclusivas a uma combinação de colunas. ou seja, para uma peoplemesa, uma combinação de first_name, last_Namee Dobdeve ser único.

Respostas:


242

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


12
Eu acho que está adicionando um índice único, não uma restrição. Ou o índice também adiciona a restrição?
Paul Cantrell

16
Não, está tudo bem. Foi mal! A restrição exclusiva vem com o índice exclusivo.
Paul Cantrell

7
Concordo com @ paul-Cantrell: não há qualquer maneira de adicionar apenas uma restrição, não um índice (que tem influências de armazenamento db)
Augustin Riedinger

17
O problema com a validação no nível do modelo é que ela não é dimensionada. Dois servidores poderiam executar os mesmos dados através de, ao mesmo tempo (como um duplo toque em um aplicativo api pesado) Eu tenho dois registros idênticos no meu DB agora mesmo e o modelo tem a validação ..
baash05

6
Eu gosto de ter os dois .. Apenas para ter certeza
baash05 17/03

25

De acordo com howmanyofme.com, "Existem 46.427 pessoas nomeadas John Smith" somente nos Estados Unidos. Isso é cerca de 127 anos de dias. Como isso está bem acima da vida útil média de um ser humano, isso significa que um conflito de DOB é matematicamente certo.

Tudo o que estou dizendo é que essa combinação específica de campos exclusivos pode levar a extrema frustração de usuário / cliente no futuro.

Considere algo realmente único, como um número de identificação nacional, se apropriado.

(Percebo que estou muito atrasado para a festa com este, mas pode ajudar futuros leitores.)


3
hm ... você certamente está certo. mas provavelmente era apenas um exemplo do que Ian queria fazer apenas para esclarecer a questão.
Eritiro 12/05

1
Talvez. A resposta não foi planejada para Ian. Ou mesmo Rangalo.
A Fader Darkly

Era para todos os fãs, não apenas Ian ou rangalo.
ARK

20

Você pode adicionar uma restrição sem um índice. Isso dependerá do banco de dados que você estiver usando. Abaixo está um código de migração de amostra para o Postgres. (tracking_number, carrier)é uma lista das colunas que você deseja usar para a restrição.

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

Existem diferentes restrições que você pode adicionar. Leia os documentos


12
Os documentos do PostgreSQL 9.4 dizem: A adição de uma restrição única criará automaticamente um índice btree exclusivo na coluna ou no grupo de colunas usadas na restrição. Uma restrição de exclusividade em apenas algumas linhas pode ser aplicada criando um índice parcial. Portanto, IMHO não há necessidade de passar para o SQL bruto quando o resultado será basicamente o mesmo que usar o add_indexmétodo. ;)
Rafał Cieślak 23/03

8
Na verdade, há um motivo: é um detalhe da implementação e desencorajado pelos documentos . Observe também que você não pode se referir à restrição por nome, pois ela não é adicionada à pg_constrainttabela.
Kaikuchn

8

Olá Você pode adicionar um índice exclusivo em sua migração às colunas, por exemplo

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

ou separar índices exclusivos para cada coluna


Desculpe, funcionou, primeiro tentei editar e migrar existente que não funcionava, depois adicionei um novo e funcionou, obrigado.
rangalo

4

No exemplo típico de uma tabela de junção entre usuários e postagens:

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

Tentar criar dois registros semelhantes gerará um erro no banco de dados (Postgres no meu caso):

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"

por exemplo, fazendo isso:

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

Exemplo totalmente executável: https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

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


4

Por uma questão de integridade, e para evitar confusão, aqui estão três maneiras de fazer a mesma coisa:
Adicionando uma restrição exclusiva nomeada a uma combinação de colunas no Rails 5.2+

Digamos que temos a tabela Locais que pertence a um anunciante e tem a coluna reference_code e você deseja apenas 1 código de referência por anunciante. então você deseja adicionar uma restrição exclusiva a uma combinação de colunas e nomeá-la.

Faz:

rails g migration AddUniquenessConstraintToLocations

E faça sua migração parecer algo como este 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

OU esta versão do bloco.

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 esta versão SQL bruta

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

Qualquer um destes terá o mesmo resultado, verifique seu schema.rb

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.