这是我在Postgres遇到的一个奇怪的,不受欢迎的行为: 当我使用复合主键创建Postgres表时,它会对复合组合的每一列强制执行NOT NULL约束。
例如,
CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));
对列NOT NULL
和m_id
强制x_id
约束,这是我不想要的!
MySQL不会这样做。我认为甲骨文也不会这样做。
我了解PRIMARY KEY
会自动执行UNIQUE
和NOT NULL
,但这对单列主键有意义。在多列主键表中,唯一性由组合确定。
有什么简单的方法可以避免Postgres的这种行为吗? 如果我执行这个:
CREATE TABLE distributors (m_id integer, x_id integer);
我当然没有任何NOT NULL
限制。
答案 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
值等同于禁止此类"重复",我会看到两个选项:
另外到上面的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,则会迅速失控 相关:
而不是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数据库可能不遵循此规则。 因此在开发可移植的应用程序时要小心。
大胆强调我的。