(Rails 4.2.1,Sqlite3)我有三个型号 - M1,M2,M3。
M1 belongs_to M2
M1也属于M3
M1有一个:name(字符串)字段。
我有以下必须验证的约束:
1)M1记录可以关联M2或M3,但不能同时关联。
2)在指定的M2或M3中,M1名称必须是唯一的。
我在模型中实现了约束(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_1和m1_2中的m3_id都为零,不确定它是否相关。
为什么没有检查约束?
答案 0 :(得分:1)
在这两种情况下,m3_id
都是NULL
吗?在Sqlite3中,空值被视为与唯一索引的上下文中的另一个空值不同。
出于唯一索引的目的,将考虑所有NULL值 与所有其他NULL值不同,因此是唯一的
请参阅https://sqlite.org/lang_createindex.html
我认为在MySQL和Postgres中也是如此。
答案 1 :(得分: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。)