有一种流派认为在关系数据库中不应该允许空值。也就是说,表的属性(列)不应允许空值。来自软件开发背景,我真的不明白这一点。似乎如果null在属性的上下文内有效,则应允许该值。这在Java中非常普遍,在Java中对象引用通常为null。没有丰富的数据库经验,我想知道我是否在这里缺少什么。
有一种流派认为在关系数据库中不应该允许空值。也就是说,表的属性(列)不应允许空值。来自软件开发背景,我真的不明白这一点。似乎如果null在属性的上下文内有效,则应允许该值。这在Java中非常普遍,在Java中对象引用通常为null。没有丰富的数据库经验,我想知道我是否在这里缺少什么。
Answers:
从数据库规范化的角度看,空值是负面的。这样的想法是,如果值不能为空,那么您实际上应该将其拆分为另一个稀疏表,这样就不需要没有值的项目的行。
这是确保所有数据有效且有价值的一种努力。
不过,在某些情况下,使用null字段很有用,尤其是在出于性能原因而希望避免再进行联接的情况下(尽管数据库引擎安装正确,这不应该成为问题,除非在特殊的高性能情况下。)
-亚当
反对null的一种说法是它们没有明确定义。如果字段为空,则可以解释为以下任何一种:
一些架构设计人员要求所有值和数据类型都应具有定义明确的解释,因此null不好。
这取决于。
只要您了解为什么允许NULL
在数据库中使用s(需要在每个列中进行选择),以及如何解释,忽略或以其他方式处理它们,它们就很好。
例如,一列类似NUM_CHILDREN
-如果不知道答案该怎么办-应该是NULL
。在我看来,此列的设计没有其他最佳选择(即使您有一个标志来确定该NUM_CHILDREN
列是否有效,您仍然必须在该列中有一个值)。
另一方面,如果您不允许NULL
s且在某些情况下(而不是标志)具有特殊的保留值,例如在真正未知的情况下子代数为-1,则必须以类似的方式解决这些问题,约定条款,文档等
因此,最终必须通过约定,文档和一致性来解决这些问题。
正如上述答案中的Adam Davis所明显支持的那样,另一种选择是将列标准化为稀疏表(在该NUM_CHILDREN
示例或其中大多数数据具有已知值的任何示例的情况下,不是稀疏表),同时能够消除所有NULL,通常是不可行的。
在许多情况下,如果属性是未知的,则为每一列连接到另一个表几乎没有意义,这可能允许NULL
使用更简单的设计。连接的开销,主键的空间要求在现实世界中意义不大。
这使我想到了通过添加基数列可以消除重复行的方式,而它从理论上解决了没有唯一键的问题,在实践中有时是不可能的(例如在大规模数据中)。纯粹主义者随后很快提出了替代PK的建议,但是从关系理论的角度来看,无意义的替代可以在关系(表)中形成元组(行)的一部分的想法是可笑的。
空标记很好。的确如此。
对于NULL的使用有几种不同的反对意见。一些反对意见是基于数据库理论的。从理论上讲,理论与实践之间没有区别。实际上,有。
完全标准化的数据库完全可以完全没有NULLS的存在。必须忽略数据值的任何地方都是可以保留整行而不会丢失信息的地方。
实际上,将表分解到这种程度并没有太大的用处,并且对数据库执行简单的CRUD操作所需的编程变得更加乏味且容易出错,而不是更少。
在某些地方使用NULL可能会导致问题:本质上,这些问题围绕以下问题进行:丢失数据的真正含义是什么?NULL真正传达的所有信息是,给定字段中没有存储任何值。但是,应用程序程序员从丢失的数据中得出的推论有时是不正确的,这会导致很多问题。
出于各种原因,某个位置可能会丢失数据。这里有一些:
该数据在这种情况下不适用。例如,单身人士的配偶名字。
数据输入表单的用户将字段留空,并且应用程序不需要在该字段中输入。
数据已从其他数据库或文件复制到数据库,并且源中缺少数据。
外键中编码有一个可选关系。
空字符串存储在Oracle数据库中。
以下是一些有关何时避免使用NULL的准则:
如果在正常的预期编程过程中,查询编写者必须编写大量ISNULL,NV,COALESCE或类似的代码,以将有效值替换为NULL。有时,最好在存储时进行替换,前提是要存储的是“真实”。
如果由于计数包含NULL的行而导致计数可能关闭。通常,可以通过仅选择count(MyField)而不是count(*)来避免这种情况。
这是一个让您更好地习惯NULL并进行相应编程的地方:每当您开始使用外部联接时,例如LEFT JOIN和RIGHT JOIN。与内部联接不同的是,外部联接后面的全部要点是当缺少某些匹配数据时获取行。丢失的数据将以NULLS形式给出。
我的底线:在不了解理论的情况下不要忽略理论。但是要学习何时脱离理论以及如何遵循理论。
将NULL用于数据字段没有任何问题。将键设置为null时必须小心。主键绝不能为NULL。外键可以为空,但是您必须小心不要创建孤立记录。
如果某些内容“不存在”,则应使用NULL而不是空字符串或其他类型的标志。
还有使用“ N / A”或“ N / K”或空字符串的另一种方法-单独的表。
例如,我们是否知道客户的电话号码:
CREATE TABLE Customer (ID int PRIMARY KEY, Name varchar(100) NOT NULL, Address varchar(200) NOT NULL);
CREATE TABLE CustomerPhone (ID int PRIMARY KEY, Phone varchar(20) NOT NULL, CONSTRAINT FK_CustomerPhone_Customer FOREIGN KEY (ID) REFERENCES Customer (ID));
如果我们不知道电话号码,我们只是不向第二张表添加一行。
我会说绝对应该使用Null。没有其他正确的方法来表示数据不足。例如,使用空字符串表示缺少的地址行是错误的,或者使用0表示缺少的年龄数据项是错误的。因为空字符串和0都是数据。空是表示这种情况的最好方法。
不要低估通过使字段为NULL可创建的复杂性。例如,以下where子句看起来将匹配所有行(位只能是1或0,对吗?)
where bitfield in (1,0)
但是,如果位字段可为NULL,它将丢失一些。或接受以下查询:
select * from mytable
where id not in (select id from excludetable)
现在,如果排除表包含一个空值和一个1,则表示:
select * from mytable
where id <> NULL and id <> 1
但是,“ id <> NULL”对于任何id值都是false,因此它将永远不会返回任何行。这甚至使经验丰富的数据库开发人员大吃一惊。
鉴于大多数人可能会因为NULL而措手不及,因此我会尽量避免使用NULL。
这是一大堆蠕虫,因为NULL可能意味着很多事情:
其中一些可以通过规范化来避免,其中一些可以通过在该列中存在值来避免(“ N / A”),其中一些可以通过使用单独的列来解释NULL的存在来缓解。 (“ N / K”,“ N / A”等)。
这也是蠕虫病毒的罐头,因为找到它们所需的SQL语法与非空值的SQL语法不同,很难对它们进行联接,并且它们通常不包含在索引条目中。
由于前面的原因,您将发现不可避免的情况。
由于后一个原因,您仍应尽最大努力减少它们的数量。
无论如何,请始终使用NOT NULL约束来防止需要值的空值。
空值的主要问题是它们具有特殊的语义,可以通过比较,聚合和联接产生意外结果。
没有东西等于null,也没有东西等于或大于null,因此,如果要进行批量比较,则必须将null设置为占位符值。
对于可能在联接中使用的组合键,这也是一个问题。如果自然键包含可为空的列,则您可能需要考虑使用合成键。
空值可能会超出计数范围,这可能不是您想要的语义。
可以联接的列中的空值将消除内部联接中的行。通常,这可能是理想的行为,但它可能会给举报人员打下陷阱。
null还有很多其他的细微之处。Joe Celko的《SQL for Smarties》一书中有整整一章,是一本好书,仍然值得一读。空值是一个很好的解决方案的地方的一些例子是:
可能存在或可能不存在联合实体的可选关系。空是在外键列上表示可选关系的唯一方法。
您可能希望使用null来减少计数的列。
可能存在或可能不存在的可选数字(例如货币)值。数字系统中没有“未记录”的有效占位符值(特别是在零是合法值的情况下),因此null确实是实现此目的的唯一好方法。
您可能希望避免使用null的地方的一些示例,因为它们可能会引起细微的错误。
带有参考表的FK的代码字段上的“未记录”值。使用占位符值,这样您(或后面的一些随机业务分析师)在对数据库进行查询时,不会无意中从结果集中删除行。
没有输入任何内容的说明字段-空字符串(''
)可以正常工作。这省去了将空值视为特殊情况的麻烦。
报告或数据仓库系统上的可选列。对于这种情况,请在维度中为“未记录”创建一个占位符行,然后加入该行。这样可以简化查询,并且可以与即席报告工具很好地配合使用。
同样,Celko的书很好地处理了这个问题。
关于法式的最好了解是,它们是指南,不应牢牢遵守指南。当学术界与现实世界发生冲突时,您很少会发现许多幸存的学术界战士。
这个问题的答案是可以使用null。如果您认为空值与实际值的比率过高,则只需评估您的情况并确定是要它们显示在表中还是将数据折叠到另一个相关的表中即可。
正如朋友喜欢说的:“不要让完美成为善良的敌人”。想伏尔泰也这样说。8)
对于数据库,null表示“我对此没有值”。这意味着(有趣的是)一个允许为空的布尔值列是完全可以接受的,并且出现在许多数据库模式中。相反,如果您的代码中有一个布尔值可以为'true','false'或'undefined'的布尔值,则您迟早可能会在thedailywtf上看到代码:)
因此,是的,如果您需要允许一个字段根本没有任何值的可能性,那么在该列上允许空值是完全可以接受的。它比潜在的替代方案(空字符串,零等)要好得多
空值可能很难使用,但是在某些情况下它们很有意义。
假设您有一个发票表,其中的列“ PaidDate”具有日期值。在支付发票之前,您在该栏中输入了什么(假设您事先不知道何时支付发票)?不能为空字符串,因为这不是有效日期。给它一个任意日期(例如1/1/1900)是没有意义的,因为该日期根本不正确。似乎唯一合理的值是NULL,因为它没有值。
在数据库中使用空值有一些挑战,但是数据库可以很好地处理它们。真正的问题是当您将数据库中的空值加载到应用程序代码中时。那就是我发现事情更加困难的地方。例如,在.NET中,强类型数据集中的日期(模仿您的数据库结构)是一个值类型,并且不能为null。因此,您必须构建变通办法。
如果可以,请避免使用null,但是不要排除null,因为它们有有效的用途。
一个陷阱,如果您使用的是Oracle数据库。如果将空字符串保存到CHAR类型的列中,则Oracle会在不询问的情况下将值强制为NULL。因此,在Oracle中避免在字符串列中使用NULL值可能非常困难。
如果使用的是NULL值,请学习使用SQL命令COALESCE,尤其是字符串值。然后,您可以防止NULL值传播到您的编程语言中。例如,假设一个人有一个FirstName,MiddleName和FamilyName,但是您想返回一个字段;
SELECT FullName = COALESCE(FirstName + ' ', '') + COALESCE(MiddleName+ ' ', '') + COALESCE(FamilyName, '') FROM Person
如果您不使用COALESCE,则任何列包含NULL值都将返回NULL。
从技术上讲,在关系数据库所基于的关系数学中,空值是非法的。因此,从纯粹的技术,语义关系模型的角度来看,不,它们并不可行。
在现实世界中,非规范化和某些违反模型的行为是可以的。但是,总的来说,空值指示您应该更仔细地查看整体设计。
我总是非常警惕null,并尽可能地将它们归一化。但这并不意味着它们有时并不是最佳选择。但是我肯定会倾向于“没有空值”,除非您真的确定在您的特定基础上使用空值会更好。
空岩石。如果在某些情况下没有必要,SQL将不具有IS NULL和IS NOT NULL作为特殊情况的运算符。NULL是概念通用性的根,其他所有内容都不是NULL。只要有可能缺少但不会遗漏数据值,就可以自由使用NULL。如果默认值始终始终正确,则默认值只能补偿NULL。例如,如果我有一个单一字段“ IsReady”,则使该字段具有默认值false和NULL的默认值可能是很有意义的,但这暗含了断言我们知道一切还没有准备好,实际上我们可能还没有这样的知识。在工作流场景中,可能决定是否准备就绪的人还没有机会发表自己的意见,因此,默认的false可能实际上很危险,导致他们忽略了似乎具有已制作,但实际上仅是默认设置。
顺便说一句,在中间名缩写的例子中,我父亲没有中间名,因此他的中间名缩写为NULL-不是空白,空格或星号-在陆军中,他的中间名缩写为NMI = No Middle初始。那有多傻?
我个人认为,仅当您将字段用作另一个表的外键时,才应使用空值,以表示该记录未链接到另一个表中的任何内容。除此之外,我发现在对应用程序逻辑进行编程时,null值实际上非常麻烦。因为在大多数编程语言中,对于许多数据类型,没有直接表示数据库为空的数据库,所以最终会创建大量应用程序代码来处理这些空值的含义。当DB遇到空整数,并尝试向其添加值1(又名空+1)时,数据库将返回空,因为这是定义逻辑的方式。但是,当编程语言尝试添加null和1时,通常会引发异常。因此,当值为null时,您的代码最终会检查执行该操作,
似乎如果null在属性的上下文内有效,则应允许该值。
但是null是什么意思呢?就是这样。它是“无价值的”,但是有十多种不同的原因可能在那里没有价值,“ null”在这种情况下并没有给您任何提示。(尚未设置,不适用于此实例,不适用于此类型,未知,不知道,未找到,错误,程序错误,...)
这在Java中非常普遍,在Java中对象引用通常为null。
有一种流派说空引用在那里也很糟糕。同样的问题:null是什么意思?
IIRC,Java同时具有“ null”和“ uninitialized”(尽管后者没有语法)。因此,戈斯林意识到对每种“无价值”使用“空”是愚蠢的。但是为什么只停两个呢?