复合PRIMARY KEY对涉及的列强制执行NOT NULL约束

时间:2014-10-30 00:05:54

标签: postgresql database-design null unique primary-key

这是我在Postgres遇到的一个奇怪的,不受欢迎的行为: 当我使用复合主键创建Postgres表时,它会对复合组合的每一列强制执行NOT NULL约束。

例如,

CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));

对列NOT NULLm_id强制x_id约束,这是我不想要的! MySQL不会这样做。我认为甲骨文也不会这样做。

我了解PRIMARY KEY会自动执行UNIQUENOT NULL,但这对单列主键有意义。在多列主键表中,唯一性由组合确定。

有什么简单的方法可以避免Postgres的这种行为吗? 如果我执行这个:

CREATE TABLE distributors (m_id integer, x_id integer);

我当然没有任何NOT NULL限制。

1 个答案:

答案 0 :(得分:34)

如果您需要允许NULL值,请使用UNIQUE constraint而不是PRIMARY KEY(并添加代理PK列,我建议serial) 。这允许列为NULL:

CREATE TABLE distributor (
   distributor_id serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , UNIQUE(m_id, x_id)
);

注意,但是(per documentation):

  

出于唯一约束的目的,空值不被视为相等。

在您的情况下,您可以在不违反约束的情况下,为(1, NULL)多次输入(m_id, x_id)。根据SQL标准中的定义,Postgres从未考虑两个NULL值相等

如果您需要将NULL值等同于禁止此类"重复",我会看到两个选项

1。两个部分索引

另外到上面的UNIQUE约束:

CREATE UNIQUE INDEX dist_m_uni_idx ON distributor (m_id) WHERE x_id IS NULL;
CREATE UNIQUE INDEX dist_x_uni_idx ON distributor (x_id) WHERE m_id IS NULL;

但是,如果两列以上的列可能为NULL,则会迅速失控 相关:

2。表达式

上的多列UNIQUE索引

而不是UNIQUE约束。我们需要一个永远不会出现在相关列中的免费默认值,例如-1。添加CHECK约束以禁止它:

CREATE TABLE distributor (
   distributor serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , CHECK (m_id <> -1)
 , CHECK (x_id <> -1)
);
CREATE UNIQUE INDEX distributor_uni_idx ON distributor (COALESCE(m_id, -1)
                                                      , COALESCE(x_id, -1))

某些RDBMS如何处理事情并不总是正确行为的有用指标。 Postgres manual hints at this

  

这意味着即使存在唯一约束,也是可能的   存储至少包含一个空值的重复行   受约束的列。此行为符合SQL标准,   但我们听说其他SQL数据库可能不遵循此规则。   因此在开发可移植的应用程序时要小心。

大胆强调我的。