【问题标题】:A migration to add unique constraint to a combination of columns向列组合添加唯一约束的迁移
【发布时间】:2011-03-23 03:59:50
【问题描述】:

我需要迁移以将唯一约束应用于列组合。即对于people 表,first_namelast_NameDob 的组合应该是唯一的。

【问题讨论】:

  • 仅供阅读此问题的任何人参考:不要根据名字、姓氏和出生日期进行限制,因为这三个都可以匹配多个人。 (这在答案中提到,但它不是真正的答案。)

标签: ruby-on-rails database


【解决方案1】:

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

【讨论】:

  • 我认为这是添加唯一索引,不是约束。或者索引是否也添加了约束?
  • 不,一切都很好。我的错!唯一约束带有唯一索引。
  • 我同意@paul-cantrell:没有办法只添加一个约束,而不是一个索引(它具有数据库存储影响)
  • 模型级验证的问题在于它无法扩展。两台服务器可以同时运行相同的数据(比如双击一个 api 繁重的应用程序)我现在在我的数据库中有两条相同的记录,并且模型具有验证..
  • 我喜欢两者兼得……只是为了确保
【解决方案2】:

您可能想要添加不带索引的约束。这取决于您使用的数据库。下面是 Postgres 的示例迁移代码。 (tracking_number, carrier) 是您要用于约束的列的列表。

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

您可以添加不同的约束。 Read the docs

【讨论】:

  • Docs for PostgreSQL 9.4 say: 添加唯一约束将自动在约束中使用的列或列组上创建唯一的 btree 索引。可以通过创建部分索引来强制执行仅对某些行的唯一性约束。 因此,恕我直言,当结果与使用 add_index 方法基本相同时,无需使用原始 SQL。 ;)
  • 其实有一个原因:这是一个实现细节,docs 不鼓励。另请注意,您不能按名称引用约束,因为它没有添加到 pg_constraint 表中。
【解决方案3】:

根据 howmanyofme.com 的数据,仅在美国,“就有 46,427 人叫 John Smith”。那是大约127年的日子。由于这远远超过了人类的平均寿命,这意味着 DOB 冲突在数学上是确定的。

我想说的是,独特字段的特定组合可能会在未来导致用户/客户极度沮丧。

考虑一些真正独特的东西,比如国家身份证号码,如果合适的话。

(我意识到我参加这个聚会很晚了,但它可以帮助未来的读者。)

【讨论】:

  • hrm...你当然是对的。但可能这只是 Ian 想要做的事情的一个例子,只是为了让问题更清楚。
  • 也许吧。不过,答案并不是为伊恩准备的。或者确实是兰加洛。
  • 它适用于所有 foo-s,而不仅仅是 Ian 或 rangalo。
【解决方案4】:

为了完整起见,为了避免混淆,这里有 3 种方法可以做同样的事情:
在 Rails 5.2+ 中的列组合中添加命名的唯一约束

假设我们有属于某个广告客户的 Locations 表,并且有列 reference_code,您只希望每个广告客户有 1 个参考代码。因此,您想为列组合添加唯一约束并为其命名。

做:

rails g migration AddUniquenessConstraintToLocations

让您的迁移看起来像这样一个班轮:

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

或这个块版本。

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

或这个原始 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

其中任何一个都会有相同的结果,请检查您的schema.rb

【讨论】:

    【解决方案5】:

    您好,您可以在迁移中为列添加唯一索引,例如

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

    或为每列单独的唯一索引

    【讨论】:

    • 对不起,它成功了,首先我尝试通过编辑和现有的迁移,但没有成功,然后添加了一个新的,它成功了,谢谢。
    【解决方案6】:

    在用户和帖子之间的连接表的典型示例中:

    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
    

    尝试创建两条相似的记录会引发数据库错误(在我的例子中是 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"
    

    例如这样做:

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

    完全可运行的例子:https://gist.github.com/Dorian/9d641ca78dad8eb64736173614d97ced

    db/schema.rb 生成:https://gist.github.com/Dorian/a8449287fa62b88463f48da986c1744a

    【讨论】:

      猜你喜欢
      • 2013-03-25
      • 1970-01-01
      • 1970-01-01
      • 2017-04-23
      • 2022-01-18
      • 2012-05-17
      • 2015-03-31
      • 1970-01-01
      • 2013-05-11
      相关资源
      最近更新 更多