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

use*_*637 20 postgresql null database-design unique primary-key

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

例如,

CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));
Run Code Online (Sandbox Code Playgroud)

强制NOT NULL列上的约束m_idx_id,我不想!MySQL没有这样做.我认为Oracle也不会这样做.

据我所知,PRIMARY KEY强制执行UNIQUENOT NULL自动执行但对单列主键有意义.在多列主键表中,唯一性由组合确定.

有没有简单的方法来避免Postgres的这种行为?
如果我执行这个:

CREATE TABLE distributors (m_id integer, x_id integer);
Run Code Online (Sandbox Code Playgroud)

NOT NULL当然没有任何限制.

Erw*_*ter 39

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

CREATE TABLE distributor (
   distributor_id serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , UNIQUE(m_id, x_id)
);
Run Code Online (Sandbox Code Playgroud)

但请注意(根据文档):

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

在你的情况,你可以输入(1, NULL)(m_id, x_id)不违反约束任意次数.Postgres从不认为两个NULL值相等 - 根据SQL标准中的定义.

如果您需要将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;
Run Code Online (Sandbox Code Playgroud)

但是,如果两列以上的列可以为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)
);
Run Code Online (Sandbox Code Playgroud)
CREATE UNIQUE INDEX distributor_uni_idx ON distributor (COALESCE(m_id, -1)
                                                      , COALESCE(x_id, -1))
Run Code Online (Sandbox Code Playgroud)

某些RDBMS如何处理事情并不总是正确行为的有用指标.在Postgres的本手册的提示:

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

大胆强调我的.

  • 为什么它丑陋?op需要唯一约束和空值. (3认同)
  • MySQL 在这方面的行为与 Postgres 完全相同。OP 的说法是错误的。 (2认同)