2016-01-10 1 views
1

(Rails 4.2.1, Sqlite3) У меня есть три модели - M1, M2, M3.Уникальный индекс, когда один из двух столбцов равен нулю

M1 M2 belongs_to

M1 и M3 belongs_to

М1 имеет: имя (строка) поле.

у меня есть следующие ограничения, которые должны быть проверены:

1) М1 запись может иметь М2 или М3 связаны, но не оба.

2) Имя M1 должно быть уникальным в зависимости от того, какой из M2 или M3 указан.

Я применил ограничение (1) в модели и работает как ожидалось. (Я упоминаю это только потому, что это может иметь отношение к сценарию).

Для ограничения (2), я добавил индекс в миграции следующим образом:

add_index :m1s, [:name, :m2_id, :m3_id], unique: true, name: "idx_m1_name" 

Тогда я вызываю:

> m2 = M2.create! # success 
> m1_1 = M1.create!(name: 'm1_1', m2: m2) #success 
> m1_2 = M1.create!(name: 'm1_1', m2: m2) # this line should fail, but doesn't 

m1_1 и m1_2 получить создан - Я ожидаю, что m1_2 должен терпеть неудачу из-за ограничения единственности.

Я проверил, что индекс добавляется, как и ожидалось. Кроме того, в соответствии с ограничением 1 m3_id равно nil как в m1_1, так и в m1_2, не уверен, что это релевантно.

Почему ограничение не проверяется?

ответ

1

So m3_idNULL в обоих случаях? В Sqlite3 нулевое значение считается отличным от другого нулевого значения в контексте уникального индекса.

Для целей уникальных индексов, все нулевые значения считаются отличной от всех остальных значений NULL и, таким образом, уникальный

См https://sqlite.org/lang_createindex.html

Я думаю, что это то же самое в MySQL и Postgres тоже.

+0

Спасибо, я подозревал нулевое значение, но ваш ответ и ссылка подтверждают это :-)! Итак, как я могу проверить ограничение единственности (2) выше в базе данных? – Anand

+0

hmm, так что вы можете иметь M2 или M3, но не оба. Вы считали, что вместо этого используете полиморфные отношения? http://guides.rubyonrails.org/association_basics.html#polymorphic-associations –

+0

https://stackoverflow.com/questions/12094479/how-to-set-unique-constraint-over-multiple-column-when-any- возможно одно-can-be-null-in-sq, но hacky –

0

Вы можете выразить это двумя индексами.

add_index :m1s, [:name, :m2_id], unique: true 
add_index :m1s, [:name, :m3_id], unique: true 

Поскольку обнуляет считаются различными для единственности целей, первый индекс не будет ограничивать строки, в которых только m3_id устанавливается, и наоборот, для второго индекса.

(Вы также можете быть заинтересованы в CHECK constraint, который проверяет, что именно один из m2_id или m3_id равна нулю. В Rails CHECK ограничения имеют незначительный нюанс в requiring the schema file to be expressed in SQL. Для более общее решение «индексы, которые применяются только к некоторым строки ", см. partial indices.)

+0

спасибо за предложения. Подход двух индексов будет успешным, даже если m2_id и m3_id равны нулю. Требование, чтобы схема в SQL выглядела слишком много для текущей проблемы, поэтому ограничение CHECK не работает. – Anand

+0

Вы можете проверить, что оба значения не равны нулю при проверке Rails. Такая проверка не склонна к расам (например, к проверке уникальности) (https://robots.thoughtbot.com/the-perils-of-uniqueness-validations). Я открыл [вопрос] (https://github.com/rails/rails/issues/23083) о ограничениях CHECK. – mpartel

Смежные вопросы