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的 列。