【问题标题】:Unique index when one of two columns is null两列之一为空时的唯一索引
【发布时间】:2016-04-14 17:10:03
【问题描述】:

(Rails 4.2.1,Sqlite3)我有三个模型 - M1、M2、M3。

M1 属于 M2

M1也属于M3

M1 有一个 :name (字符串)字段。

我有以下必须验证的约束:

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 中都是 nil,不确定是否相关。

为什么不检查约束?

【问题讨论】:

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


    【解决方案1】:

    所以m3_id 在这两种情况下都是NULL 吗?在 Sqlite3 中,空值被认为与唯一索引上下文中的另一个空值不同。

    出于唯一索引的目的,所有 NULL 值都被视为 不同于所有其他 NULL 值,因此是唯一的

    https://sqlite.org/lang_createindex.html

    我认为在 MySQL 和 Postgres 中也是如此。

    【讨论】:

    • 谢谢,我怀疑是空值,但您的回复和链接证实了这一点:-)!那么如何在数据库中验证上面的唯一性约束(2)呢?
    • 嗯,所以你可以拥有 M2 或 M3,但不能同时拥有。您是否考虑过使用多态关系? guides.rubyonrails.org/…
    • stackoverflow.com/questions/12094479/… 也是可能的,但是很老套
    • 多态不适用于我的场景。我有一个类似于嵌套帖子的博客。所以,M1 就像一个帖子,M2 就像一个博客,而 M3 实际上是一个父帖子。因此,帖子可以是博客中的根帖子,也可以是另一个帖子的子帖子,但不能两者兼而有之。
    • 是的,我同意另一个是hacky。我可能会在 rails 的模型中创建验证,而不是更改默认的空值。我不能真正制作这些其他值,因为它们都是外键 ID,并且将它们视为非 null 会引发各种问题。这一定是一个相当普遍的问题。我想知道是否有一些优雅的方法来解决它。
    【解决方案2】:

    你可以用两个索引来表达。

    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。)

    【讨论】:

    • 感谢您的建议。即使 m2_id 和 m3_id 都为空,这两个索引方法也会成功。对于当前的问题来说,要求架构在 SQL 中似乎太过分了,所以 CHECK 约束不起作用。
    • 您可以在 Rails 验证中检查两者都不为空。这样的验证不容易出现比赛like uniqueness validations are。我已经打开了一个关于 CHECK 约束的issue
    猜你喜欢
    • 2014-07-16
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 2012-07-03
    相关资源
    最近更新 更多