Ограничение уникальности 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 будут добавлены два нулевых значения. Я мог просто просто опустить индекс в миграции, но тогда есть вероятность, что я получу два PaymentAgreements с одним и тем же product_id, как показано здесь: Concurrency и целостность
Мой вопрос - это лучший/наиболее распространенный способ решения этой проблемы.
Ответы
Ответ 1
это зависит от вашего сервера db.
как для mysql:
УНИКАЛЬНЫЙ индекс создает ограничение так что все значения в индексе должны быть отличным. Ошибка возникает, если вы попробуйте добавить новую строку с ключевым значением который соответствует существующей строке. Эта ограничение не применяется к NULL значения, за исключением хранилища BDB двигатель. Для других двигателей a UNIQUE index допускает несколько значений NULL для столбцы, которые могут содержать NULL.
Ответ 2
Некоторые основные системы баз данных не позволяют уникальному индексу содержать несколько NULL: уникальная применима к 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 в наиболее часто используемых механизмах хранения.)