2016-01-10 121 views
1

(Rails 4.2.1,Sqlite3)我有三个模型 - M1,M2,M3。当两列之一为空时的唯一索引

M1 M2 belongs_to的

M1也belongs_to的M3

M1有:名称(字符串)字段。

我有必须验证以下约束:

1)一种M1记录可以具有M2或M3相关,但不能同时使用。

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在m1_1和m1_2中均为零,不确定是否相关。

为什么约束没有被检查?

回答

1

因此m3_idNULL在这两种情况下?在Sqlite3中,在唯一索引的上下文中,空值被认为与另一个空值不同。

为唯一指标的目的,所有的NULL值被认为 所有其他NULL值不同,因此是唯一

https://sqlite.org/lang_createindex.html

我认为这是在MySQL和同Postgres也是。

+0

谢谢,我怀疑null值,但你的回应和链接证实它:-)!那么如何在数据库中验证上面的唯一性约束(2)? – Anand

+0

嗯,所以你可以有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-一个可以为零的平方也是可能的,但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)。我已经打开了关于CHECK约束的[issue](https://github.com/rails/rails/issues/23083)。 – mpartel