两列中的一列为空时的唯一索引

时间:2016-01-10 09:12:58

标签: ruby-on-rails unique-constraint database-indexes

(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都为零,不确定它是否相关。

为什么没有检查约束?

2 个答案:

答案 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_idm3_id中的一个是否为空。在Rails中,CHECK约束有一个小的警告requiring the schema file to be expressed in SQL。有关“仅适用于某些行的索引”的更一般解决方案,请参阅partial indices。)