Una migración para agregar restricciones únicas a una combinación de columnas

140

Lo que necesito es una migración para aplicar una restricción única a una combinación de columnas. es decir, para una peoplemesa, una combinación de first_name, last_Namey Dobdebe ser único.

rangalo
fuente

Respuestas:

242

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

Robert Speicher
fuente
12
Creo que eso agrega un índice único, no una restricción. ¿O el índice agrega también la restricción?
Paul Cantrell
16
No, está todo bien. ¡Culpa mía! La restricción única viene con el índice único.
Paul Cantrell
77
Estoy de acuerdo con @ paul-cantrell: no hay forma de agregar solo una restricción, no un índice (que tiene influencias de almacenamiento de db)
Augustin Riedinger
17
El problema con la validación a nivel de modelo es que no se escala. Dos servidores podrían ejecutar los mismos datos al mismo tiempo (como un doble toque en una aplicación API pesada) Tengo dos registros idénticos en mi base de datos en este momento y el modelo tiene la validación ..
baash05
66
Me gusta tener ambos ... Solo para asegurarme
baash05
25

Según howmanyofme.com, "hay 46,427 personas llamadas John Smith" solo en los Estados Unidos. Eso es alrededor de 127 años de días. Como esto está muy por encima del promedio de vida de un ser humano, esto significa que un choque DOB es matemáticamente seguro.

Todo lo que digo es que esa combinación particular de campos únicos podría conducir a una frustración extrema entre usuarios y clientes en el futuro.

Considere algo que sea realmente único, como un número de identificación nacional, si corresponde.

(Me doy cuenta de que llego muy tarde a la fiesta con este, pero podría ayudar a los futuros lectores).

Un fader oscuro
fuente
3
hrm ... ciertamente tienes razón. pero probablemente fue solo un ejemplo de lo que Ian quería hacer para aclarar la pregunta.
eritiro
1
Tal vez. Sin embargo, la respuesta no estaba destinada a Ian. O de hecho Rangalo.
A Fader Darkly
Estaba destinado a todos los foo-s, no solo a Ian o rangalo.
ARCA
20

Es posible que desee agregar una restricción sin un índice. Esto dependerá de la base de datos que esté utilizando. A continuación se muestra un código de migración de muestra para Postgres. (tracking_number, carrier)es una lista de las columnas que desea usar para la restricción.

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

Hay diferentes restricciones que puede agregar. Lee los documentos

Josh
fuente
12
Los documentos para PostgreSQL 9.4 dicen: Agregar una restricción única creará automáticamente un índice btree único en la columna o grupo de columnas utilizado en la restricción. Se puede aplicar una restricción de unicidad en solo algunas filas creando un índice parcial. Entonces, en mi humilde opinión, no hay necesidad de pasar a SQL sin formato cuando el resultado será básicamente el mismo que usar el add_indexmétodo. ;)
Rafał Cieślak
8
En realidad, hay una razón: es un detalle de implementación y es desalentado por los documentos . También tenga en cuenta que no puede hacer referencia a la restricción por nombre, ya que no se agrega a la pg_constrainttabla.
kaikuchn
8

Hola Puede agregar un índice único en su migración a las columnas, por ejemplo

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

o índices únicos separados para cada columna

Bohdan
fuente
Lo siento, funcionó, primero lo intenté editando y la migración existente que no funcionó, luego agregué una nueva y funcionó, gracias.
rangalo
4

En el ejemplo típico de una tabla de unión entre usuarios y publicaciones:

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

Intentar crear dos registros similares arrojará un error de base de datos (Postgres en mi 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 ejemplo haciendo eso:

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

Ejemplo completamente ejecutable: https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

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

dorio
fuente
4

Por razones de integridad y para evitar confusiones, aquí hay 3 formas de hacer lo mismo:
Agregar una restricción única con nombre a una combinación de columnas en Rails 5.2+

Supongamos que tenemos una tabla de ubicaciones que pertenece a un anunciante y tiene una columna reference_code y solo desea 1 código de referencia por anunciante. entonces desea agregar una restricción única a una combinación de columnas y nombrarla.

Hacer:

rails g migration AddUniquenessConstraintToLocations

Y haga que su migración se parezca a esta línea:

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

O esta versión de bloque.

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

O esta versión de SQL sin formato

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

Cualquiera de estos tendrá el mismo resultado, verifique su schema.rb

Khalil Gharbaoui
fuente