16

移行ファイルには次のものがあります

  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 に 2 つの null 値が挿入されると、明らかに例外がスローされます。移行で単にインデックスを省略することもできますが、次に示すように、同じ product_id を持つ 2 つの PaymentAgreements を取得する可能性があります:同時実行性と整合性

私の質問は、この問題に対処するための最良/最も一般的な方法は何ですか

4

2 に答える 2

11

それはあなたのdbサーバーに依存します。mysqlに関しては:

UNIQUE インデックスは、インデックス内のすべての値が個別でなければならないという制約を作成します。既存の行と一致するキー値を持つ新しい行を追加しようとすると、エラーが発生します。この制約は、BDB ストレージ エンジンを除き、NULL 値には適用されません。他のエンジンの場合、UNIQUE インデックスは、NULL を含むことができる列に対して複数の NULL 値を許可します。

于 2010-05-28T07:16:57.370 に答える
0

主要なデータベース システムの中には、一意のインデックスに複数の NULL を含めることを許可していないものがあります。データベース レベルでこれを回避する方法があります (たとえば、トリガーや計算列。リンク テキストを参照)。

product_idアプリケーション レベルでこれに対処し、が null でない場合に一意性をチェックする検証を組み込むことができます。

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 を許可します。)

于 2010-05-28T05:38:48.687 に答える