Rails唯一性约束和匹配空列的db唯一索引

我的迁移文件中有以下内容

def self.up create_table :payment_agreements do |t| t.boolean :automatic, :default => true, :null => false t.string :payment_trigger_on_order t.references :supplier t.references :seller t.references :product t.timestamps end end 

我想确保如果指定了product_id它是唯一的但我也想允许null所以我在我的模型中有以下内容:

  validates :product_id, :uniqueness => true, :allow_nil => true 

工作得很好,但我应该为迁移文件添加一个索引

 add_index :payment_agreements, :product_id, :unique => true 

显然,当为product_id插入两个空值时,这将抛出exception。 我可以简单地省略迁移中的索引,但是我有可能得到两个PaymentAgreements,其中包含相同的product_id: 并发性和完整性

我的问题是处理这个问题的最佳/最常用方法是什么

这取决于您的数据库服务器。 至于mysql:

UNIQUE索引创建一个约束,使索引中的所有值必须是不同的。 如果您尝试添加具有与现有行匹配的键值的新行,则会发生错误。 此约束不适用于除BDB存储引擎之外的NULL值。 对于其他引擎, UNIQUE索引允许包含NULL的列的多个NULL值。

某些主要数据库系统不允许唯一索引包含多个NULL:唯一适用于NULL和非NULL。 在数据库级别上有很多方法(例如,触发器或计算列;请参阅链接文本 )。

您可以在应用程序级别上解决此问题,并在product_id不为null时进行validation以检查唯一性。

 validate :enforce_unique_product_id def enforce_unique_product_id if (!self.product_id.nil? && PaymentAgreement.exists?(:conditions=>['product_id = ?', self.product_id])) errors.add_to_base('There is already an agreement with product id " + self.product_id) end end 

(更新:正如zed_0xff所指出的,MySql允许在最常用的存储引擎中的UNIQUE索引中使用多个NULL。)