Rom*_*kov 142 database database-design
ORACLE不允许在包含主键的任何列中使用NULL值.似乎大多数其他"企业级"系统也是如此.
同时,大多数系统还允许可空列的唯一约束.
为什么唯一约束可以有NULL但主键不能?这有一个基本的逻辑原因,还是更多的技术限制?
Tom*_*lak 206
主键用于唯一标识行.这是通过将键的所有部分与输入进行比较来完成的.
根据定义,NULL不能成功进行比较.即使与自身(NULL = NULL)的比较也会失败.这意味着包含NULL的键不起作用.
另外,外键中允许NULL,以标记可选关系.(*)在PK中允许它也会打破这个.
(*)提醒一句:拥有可空的外键不是干净的关系数据库设计.
如果有两个实体A并且可以选择与B哪些实体A相关B,那么干净的解决方案就是创建一个解析表(比方说AB).该表将连接A用B:如果是一个关系那么它将包含一个记录,如果不是那就不是.
Ton*_*ews 56
主键为表中的每一行定义唯一标识符:当表具有主键时,您有一种保证的方法可以从中选择任何行.
唯一约束不一定标识每一行; 它只是指定如果某行的列中包含值,则它们必须是唯一的.这不足以唯一地标识每一行,这是主键必须做的事情.
zxq*_*xq9 44
从根本上说,多列主键中的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));
Run Code Online (Sandbox Code Playgroud)
主键的前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));
Run Code Online (Sandbox Code Playgroud)
在这个版本中,元组的"ext"部分是NOT NULL但是默认为空字符串 - 它在语义上(实际上)与NULL不同.NULL是未知的,而空字符串是"不存在的东西"的故意定义.换句话说,"空"和"空"是不同的东西.它的区别在于"我这里没有价值"和"我不知道这里有什么价值".
当您注册缺少版本扩展的程序包时,您知道它缺少扩展名,因此空字符串实际上是正确的值.只有在您不知道是否有扩展名时,NULL才会正确.在字符串值为常态的系统中,这种情况更容易处理,因为除了插入0或1之外无法表示"空整数",这将在以后进行的任何比较中累积起来(具有它自己的含义).
顺便提一下,这两种方式在Postgres中都是有效的(因为我们讨论的是"企业"RDMBS),但是当你在混合中抛出NULL时,比较结果会有很大差异 - 因为NULL =="不知道"所以所有由于你无法知道未知的东西,所以比较的结果涉及NULL结束为NULL.因此,在排序,比较等时,这可能是微妙错误的根源.Postgres假设您是成年人,可以自己做出这个决定.Oracle和DB2假设您没有意识到您正在做一些愚蠢的事情并抛出错误.这通常是正确的,但并非总是如此 - 在某些情况下,您可能实际上不知道并且有一个NULL,因此留下一个具有未知元素的行,对于该行,有意义的比较是不正确的行为.
在任何情况下,您都应该努力消除整个模式中允许的NULL字段数,并且当涉及到作为主键一部分的字段时,应该加倍.在绝大多数情况下的NULL列的存在的指示未归一化(而不是刻意去规范化)架构设计,应该很难想到之前被接受.
Cog*_*gsy 19
NULL == NULL - > false(至少在DBMS中)
因此,即使使用具有实际值的其他列,您也无法使用NULL值检索任何关系.
Adr*_*vel 10
我仍然认为这是技术性带来的基本/功能缺陷。如果您有一个可以识别客户的可选字段,那么您现在必须在其中添加一个虚拟值,只是因为 NULL != NULL,不是特别优雅,但它是“行业标准”
小智 5
托尼安德鲁斯的答案是一个不错的答案。但真正的答案是,这是关系数据库社区使用的约定,并不是必需的。也许这是一个很好的约定,也许不是。
将任何内容与 NULL 进行比较会导致 UNKNOWN(第三个真值)。因此,正如 nulls 所建议的那样,所有关于平等的传统智慧都被排除在外。乍一看,这就是它的样子。
但我认为这不一定是这样,甚至 SQL 数据库也不认为 NULL 会破坏所有比较的可能性。
在您的数据库中运行查询 SELECT * FROM VALUES(NULL) UNION SELECT * FROM VALUES(NULL)
您看到的只是一个元组,其中一个属性的值为 NULL。因此,联合在这里将两个 NULL 值视为相等。
将具有 3 个组件的复合键与具有 3 个属性的元组进行比较时 (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL 结果是 UNKNOWN .
但是我们可以定义一种新的比较运算符,例如。==。X == Y <=> X = Y 或(X 为空且 Y 为空)
拥有这种相等运算符将使具有空组件的复合键或具有空值的非复合键没有问题。
| 归档时间: |
|
| 查看次数: |
97535 次 |
| 最近记录: |