当前位置:  开发笔记 > 数据库 > 正文

复合主键中可为空的列有什么问题?

如何解决《复合主键中可为空的列有什么问题?》经验,为你挑选了4个好方法。

ORACLE不允许在包含主键的任何列中使用NULL值.似乎大多数其他"企业级"系统也是如此.

同时,大多数系统还允许可空列的唯一约束.

为什么唯一约束可以有NULL但主键不能?这有一个基本的逻辑原因,还是更多的技术限制?



1> Tomalak..:

主键用于唯一标识行.这是通过将键的所有部分与输入进行比较来完成的.

根据定义,NULL不能成功进行比较.即使与自身(NULL = NULL)的比较也会失败.这意味着包含NULL的键不起作用.

另外,外键中允许NULL,以标记可选关系.(*)在PK中允许它也会打破这个.


(*)提醒一句:拥有可空的外键不是干净的关系数据库设计.

如果有两个实体A并且可以选择与B哪些实体A相关B,那么干净的解决方案就是创建一个解析表(比方说AB).该表将连接AB:如果一个关系那么它将包含一个记录,如果不是那就不是.


我已经改变了对这个问题的接受答案.从选票来看,这个答案对更多人来说是最清楚的.我仍然觉得Tony Andrews的答案更好地解释了这个设计背后的意图.也检查一下!
"拥有可空的外键不是干净的关系数据库设计." - 无空数据库设计(第六范式)总是增加了复杂性,所获得的节省空间通常超过了实现这些收益所需的额外程序员工作.
问:你什么时候想要一个NULL FK而不是缺少一行?答:仅在针对优化进行非规范化的模式版本中.在非平凡模式中,像这样的非标准化问题可能会在需要新功能时导致问题.otoh,网页设计人群并不在意.我至少会添加一个警告,而不是让它听起来像一个好的设计理念.

2> Tony Andrews..:

主键为表中的每一行定义唯一标识符:当表具有主键时,您有一种保证的方法可以从中选择任何行.

唯一约束不一定标识每一行; 它只是指定如果某行的列中包含值,它们必须是唯一的.这不足以唯一地标识每一行,这是主键必须做的事情.


在Sql Server中,具有可为空列的唯一约束允许该列中的值"null"仅一次(给定约束的其他列的相同值).因此,这种唯一约束本质上就像具有可空列的pk一样.
在Oracle中(我不知道SQL Server),该表可以包含许多行,其中**all**唯一约束中的列为null.但是,如果唯一约束中的某些列不为null且某些列为null,则强制执行唯一性.

3> zxq9..:

从根本上说,多列主键中的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值做出如此好的解释.谢谢!

4> Cogsy..:

NULL == NULL - > false(至少在DBMS中)

因此,即使使用具有实际值的其他列,您也无法使用NULL值检索任何关系.


错误的答案.NULL == NULL - > UNKNOWN.不是假的.问题是,如果测试结果未知,则不会认为违反了约束.这通常使它_SEEM_好像比较产生错误,但实际上并非如此.
推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有