Ограничение уникальности Rails и соответствие уникального индекса БД для нулевого столбца - PullRequest
15 голосов
/ 28 мая 2010

В моем миграционном файле есть следующее

  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 будут вставлены два нулевых значения. Я мог бы просто пропустить индекс в миграции, но тогда есть вероятность, что я получу два PaymentAgreements с тем же product_id, как показано здесь: Параллельность и целостность

Мой вопрос - каков наилучший / наиболее распространенный способ решения этой проблемы

Ответы [ 2 ]

11 голосов
/ 28 мая 2010

это зависит от вашего сервера БД. что касается mysql:

УНИКАЛЬНЫЙ индекс создает ограничение так что все значения в индексе должны быть отличным Ошибка возникает, если вы попробуйте добавить новую строку со значением ключа это соответствует существующей строке. это ограничение не относится к NULL значения, кроме хранилища BDB двигатель. Для других двигателей УНИКАЛЬНО Индекс допускает несколько значений NULL для столбцы, которые могут содержать NULL.

0 голосов
/ 28 мая 2010

Некоторые основные системы баз данных не позволяют уникальному индексу содержать несколько значений NULL: уникальный применяется как к NULL, так и к ненулевым. Есть способы обойти это на уровне базы данных (например, триггеры или вычисляемый столбец; см. текст ссылки ).

Вы можете решить эту проблему на уровне приложения и ввести проверку, которая проверяет уникальность, если 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 допускает множественные значения NULL в индексе UNIQUE в наиболее часто используемых механизмах хранения.)

...