这是我在Postgres中遇到的一个奇怪的、不想要的行为:当我创建一个带有复合主键的Postgres表时,它对复合组合的每一列强制执行NOT NULL约束。
例如,
CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));
在列m_id
和x_id
上强制NOT NULL
约束,这是我不想要的! MySQL没有这样做。我认为Oracle也没有这样做。
我知道PRIMARY KEY
会自动强制UNIQUE
和NOT NULL
,但这对于单列主键是有意义的,在多列主键表中,唯一性由组合决定。
有什么简单的方法可以避免Postgres的这种行为吗?当我执行以下命令时:
CREATE TABLE distributors (m_id integer, x_id integer);
当然,我没有得到任何NOT NULL
约束,但是我也没有主键。
2条答案
按热度按时间csga3l581#
如果您 * 需要 * 允许NULL值,请使用**
UNIQUE
约束**(或索引)而不是PRIMARY KEY
(并添加代理PK列-我建议在Postgres 10或更高版本中使用serial
或IDENTITY
列)。UNIQUE
约束允许列为NULL:The manual:
出于唯一约束的目的,空值不被视为相等,除非指定了
NULLS NOT DISTINCT
。在您的例子中,您可以为
(m_id, x_id)
输入类似(1, NULL)
的内容任意次,而不会违反约束,Postgres从不认为两个NULL值 * 相等 *-根据SQL标准中的定义。如果你需要将
NULL
值视为相等(即"非相异")来禁止这样的"重复",我看到了两个三(Postgres 15起)选项:0.
NULLS NOT DISTINCT
这个选项是随Postgres 15添加的,它允许将NULL值视为"非独特的",因此它们中的两个在唯一约束或索引中冲突。这是最方便的选项。
这意味着即使存在唯一约束,也可能存储至少一个约束列中包含空值的重复行。可以通过添加子句
NULLS NOT DISTINCT
...详细说明:
1.两个部分索引
UNIQUE
约束之外:但是,如果有两个以上的列可以为NULL,则会很快失控。请参见:
2.表达式的多列
UNIQUE
索引代替
UNIQUE
约束。我们需要一个自由的默认值,它永远不会出现在所涉及的列中,如-1
。添加CHECK
约束以禁用它:一个二个一个一个
oalqel3c2#
当您需要多态关系时
表使用的列名指示它们可能是对其它表的引用:
因此,我认为您 * 可能 * 正在尝试对与其他表的多态关系进行建模-其中表
distributors
中的记录可以引用一个m
记录 xor 一个x
记录。多态关系在SQL中是很困难的。我所看到的关于这个主题的最好的资源是“Modeling Polymorphic Associations in a Relational Database“。那里提供了四个可供选择的选项,大多数情况下的建议称为“Exclusive Belongs To”,在您的情况下,这将导致如下所示的表:
与其他解决方案一样,此解决方案使用代理主键列,因为在SQL标准中强制主键不包含
NULL
值。这个解决方案在@Erwin Brandstetter的答案中的三个选项的基础上添加了第四个选项,以避免“您可以为
(m_id, x_id)
输入类似(1, NULL)
的内容任意次而不违反约束”的情况。这里,通过两个度量的组合来排除这种情况:1.部分唯一索引分别在每列上:两个记录
(1, NULL)
和(1, NULL)
不会违反第二列上的约束,因为NULL
被认为是不同的,但是它们会违反第一列上的约束(具有值1
的两个记录)。1.**检查约束:**缺少的部分阻止了多个
(NULL, NULL)
记录,但仍然允许,因为NULL
被认为是不同的,而且无论如何,因为我们的部分索引没有覆盖它们以保存空间和写入事件。这是通过CHECK
约束实现的,它通过确保恰好一列是NULL
来阻止任何(NULL, NULL)
记录。但有一点不同@Erwin Brandstetter的答案中的所有选项都允许至少一个记录
(NULL, NULL)
和任意数量的记录在任何列中没有NULL
值(如(1, 2)
)。当建模多态关系时,您希望不允许这样的记录。这是通过上面解决方案中的检查约束实现的。