15

我已经在我的迁移文件以下为NULL列DB唯一索引Rails的唯一性约束和匹配

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它是独一无二的,但我也希望允许空,因此我已经在我的模型如下:

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

伟大的作品,但后来我应该添加一个索引来迁移文件

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

很明显,当为product_id插入两个空值时,这将引发异常。我只是简单地忽略了迁移索引,但然后有机会得到两个支付协议,具有相同的product_id,如下所示:Concurrency and integrity

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

+0

这个问题是类似于http://stackoverflow.com/questions/191421/how-to-create-a-unique-index-on-a-null-column – x1a4 2010-05-28 05:30:06

+1

validates_uniqueness_of:PRODUCT_ID,:如果= > lambda {!self.product_id.nil? } – user386660 2010-07-09 10:11:20

回答

0

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

您可以在应用程序级别解决此问题,并在验证中检查唯一性,如果product_id不为空。

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索引多个空值。)

11

这取决于你的数据库服务器上。 如为MySQL:

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