ORACLE不允许在包含主键的任何列中使用NULL值.似乎大多数其他"企业级"系统也是如此.
同时,大多数系统还允许可空列的唯一约束.
为什么唯一约束可以有NULL但主键不能?这有一个基本的逻辑原因,还是更多的技术限制?
主键用于唯一标识行.这是通过将键的所有部分与输入进行比较来完成的.
根据定义,NULL不能成功进行比较.即使与自身(NULL = NULL
)的比较也会失败.这意味着包含NULL的键不起作用.
另外,外键中允许NULL,以标记可选关系.(*)在PK中允许它也会打破这个.
(*)提醒一句:拥有可空的外键不是干净的关系数据库设计.
如果有两个实体A
并且可以选择与B
哪些实体A
相关B
,那么干净的解决方案就是创建一个解析表(比方说AB
).该表将连接A
用B
:如果是一个关系那么它将包含一个记录,如果不是那就不是.
主键为表中的每一行定义唯一标识符:当表具有主键时,您有一种保证的方法可以从中选择任何行.
唯一约束不一定标识每一行; 它只是指定如果某行的列中包含值,则它们必须是唯一的.这不足以唯一地标识每一行,这是主键必须做的事情.
从根本上说,多列主键中的NULL没有任何问题.但是有一个有设计者可能不想要的含义,这就是为什么许多系统在你尝试这个时会抛出错误的原因.
考虑存储为一系列字段的模块/包版本的情况:
CREATE TABLE module (name varchar(20) PRIMARY KEY, description text DEFAULT '' NOT NULL); CREATE TABLE version (module varchar(20) REFERENCES module, major integer NOT NULL, minor integer DEFAULT 0 NOT NULL, patch integer DEFAULT 0 NOT NULL, release integer DEFAULT 1 NOT NULL, ext varchar(20), notes text DEFAULT '' NOT NULL, PRIMARY KEY (module, major, minor, patch, release, ext));
主键的前5个元素是发布版本的常规定义部分,但是某些包具有通常不是整数的自定义扩展(例如"rc-foo"或"vanilla"或"beta"或其他任何人其中4场是不够的可能梦想).如果一个包没有扩展名,那么在上面的模型中它是NULL,并且不会因为这样做而造成伤害.
但什么是 NULL?它应该代表缺乏信息,一个未知数.也就是说,也许这更有意义:
CREATE TABLE version (module varchar(20) REFERENCES module, major integer NOT NULL, minor integer DEFAULT 0 NOT NULL, patch integer DEFAULT 0 NOT NULL, release integer DEFAULT 1 NOT NULL, ext varchar(20) DEFAULT '' NOT NULL, notes text DEFAULT '' NOT NULL, PRIMARY KEY (module, major, minor, patch, release, ext));
在这个版本中,元组的"ext"部分是NOT NULL但是默认为空字符串 - 它在语义上(实际上)与NULL不同.NULL是未知的,而空字符串是"不存在的东西"的故意定义.换句话说,"空"和"空"是不同的东西.它的区别在于"我这里没有价值"和"我不知道这里有什么价值".
当您注册缺少版本扩展的程序包时,您知道它缺少扩展名,因此空字符串实际上是正确的值.只有在您不知道是否有扩展名时,NULL才会正确.在字符串值为常态的系统中,这种情况更容易处理,因为除了插入0或1之外无法表示"空整数",这将在以后进行的任何比较中累积起来(具有它自己的含义).
顺便提一下,这两种方式在Postgres中都是有效的(因为我们讨论的是"企业"RDMBS),但是当你在混合中抛出NULL时,比较结果会有很大差异 - 因为NULL =="不知道"所以所有由于你无法知道未知的东西,所以比较的结果涉及NULL结束为NULL.因此,在排序,比较等时,这可能是微妙错误的根源.Postgres假设您是成年人,可以自己做出这个决定.Oracle和DB2假设您没有意识到您正在做一些愚蠢的事情并抛出错误.这通常是正确的,但并非总是如此 - 在某些情况下,您可能实际上不知道并且有一个NULL,因此留下一个具有未知元素的行,对于该行,有意义的比较是不正确的行为.
在任何情况下,您都应该努力消除整个模式中允许的NULL字段数,并且当涉及到作为主键一部分的字段时,应该加倍.在绝大多数情况下的NULL列的存在的指示未归一化(而不是刻意去规范化)架构设计,应该很难想到之前被接受.
NULL == NULL - > false(至少在DBMS中)
因此,即使使用具有实际值的其他列,您也无法使用NULL值检索任何关系.