繁体   English   中英

将 `null` 视为表唯一约束中的不同值

[英]Treating `null` as a distinct value in a table unique constraint

我有一个表,用于为客户端定义默认和自定义选项。 如果custom_id字段具有值,则它表示唯一自定义作业的记录。 如果它为空,则该记录代表客户端的默认选项。

我的问题是我想在两种情况下强制执行唯一性:

  1. custom_idclientoption都是非空的
  2. clientoption非空,但custom_id为空

下面的表定义适用于第一种情况,但不适用于第二种情况,因为 null 不被视为值。 有没有办法让 null 被视为一个值?

class OptionTable(Base):
    __tablename__ = "option_table"
    __table_args__ = (
        UniqueConstraint("custom", "client", "option", name="uix_custom_client_option"),
    )

    id = Column(Integer, primary_key=True)
    custom_id = Column(Integer, ForeignKey("custom.id"), nullable=True)
    client = Column(String, nullable=False)
    option = Column(String, nullable=False)

以下是一些示例数据和按顺序添加的结果:

+----+----------+----------+--------+---------------------------------------------+
| id | CustomID |  Client  | Option |                   result                    |
+----+----------+----------+--------+---------------------------------------------+
|  1 | 123      | MegaCorp | Apple  | OK                                          |
|  2 | 123      | MegaCorp | Apple  | not unique                                  |
|  3 | NULL     | MegaCorp | Apple  | OK                                          |
|  4 | NULL     | MegaCorp | Google | OK                                          |
|  5 | NULL     | MegaCorp | Google | this one should fail, but currently doesn't |
+----+----------+----------+--------+---------------------------------------------+

这个相关的答案是我正在寻找的,使用 MySQL。 理想的解决方案是使用 sqlalchemy 来做到这一点。

我会做

CREATE UNIQUE INDEX ON atable
   (client, option, coalesce(custom_id, -42));

其中-42custom_id不能出现的值。

它是如何工作的?

如果有两行具有相同的clientoptioncustom_id ,所有这些都不是NOT NULL ,它将像常规唯一索引一样工作,并阻止添加第二行。

如果有两行具有相同clientoption行都具有custom_id IS NULL ,则索引将阻止添加第二行,因为它索引-42而不是NULL ,并且两个索引元组将相同。

根据此答案中推荐的方法,解决方案是创建两个部分索引

将 sqlalchemy 用于问题中的示例,如下所示:

class OptionTable(Base):
    __tablename__ = "option_table"

    id = Column(Integer, primary_key=True)
    custom_id = Column(Integer, ForeignKey("custom.id"), nullable=True)
    client = Column(String, nullable=False)
    option = Column(String, nullable=False)

    __table_args__ = (
        Index(
            "uix_custom_client_option", 
            "custom_id", 
            "client", 
            "option", 
            unique=True, 
            postgresql_where=custom_id.isnot(None)
        ),
        Index(
            "uix_client_option", 
            "client",  
            "option", 
            unique=True, 
            postgresql_where=custom_id.is_(None)
        ),
    )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM