关系数据库中的空值是否可以?

有一种思想是在关系数据库中不应该允许使用空值。 也就是说,一个表的属性(列)不应该允许空值。 从软件开发背景来看,我真的不明白这一点。 看来,如果null在属性的上下文中是有效的,那么它应该被允许。 这在Java中非常普遍,对象引用通常是空的。 没有广泛的数据库经验,我想知道我是否在这里失去了一些东西。

从数据库规范化的angular度来看,空值是负面的。 这个想法是,如果一个值什么都不是,那么你真的应该把它分成另一个稀疏表,这样你就不需要行来处理没有值的项。

这是努力确保所有的数据是有效的和有价值的。

在某些情况下,有一个空字段是有用的,但是,尤其是当你想要避免另一个性能方面的联合(如果数据库引擎设置正确,这应该不是一个问题,除非在非常高的性能情况下)。

-亚当

空标记是好的。 真的,他们是。

反对零的一个论点是他们没有一个明确的解释。 如果一个字段为空,那么可以解释为以下任何一个:

  • 值为“无”或“空集”
  • 没有任何价值在这个领域是有意义的。
  • 价值是未知的。
  • 该值尚未input。
  • 该值是一个空string(对于不区分空值和空string的数据库)。
  • 一些特定于应用程序的含义(例如,“如果值为空,则使用默认值”)。
  • 发生错误,导致该字段实际上不应该有空值。

一些模式devise者要求所有的值和数据types应该有明确的解释,因此空值是不好的。

这取决于。

只要你明白为什么你要在数据库中允许NULL这个select需要在每个列的基础上进行 )以及你将如何解释,忽略或以其他方式处理它们,它们都是好的。

例如,像NUM_CHILDREN这样的列 – 如果你不知道答案,你会做什么 – 它应该是NULL 。 在我看来,这个列的devise没有其他的最佳select(即使你有一个标志来确定NUM_CHILDREN列是否有效,你仍然需要在这个列中有一个值)。

另一方面,如果你不允许NULL并且在某些情况下(而不是标志)有特殊的保留值,比如对于真正未知的孩子的数量,如-1,你必须以类似的方式解决这些问题,在公约,文件等方面

所以,最终,这些问题必须通过公约,文件和一致性来解决。

另外,正如Adam Davis在上面的答案中明显支持的那样,将列标准化为稀疏(或者在NUM_CHILDREN示例或大多数数据具有已知值的任何示例的情况下不稀疏),而能够消除所有NULL,在一般实践中是不可行的。

在很多情况下,一个属性是未知的,join到另一个表的每一列,这可能允许一个更简单的deviseNULL是没有意义的。 连接的开销,主键的空间要求在现实世界中是没有意义的。

这让人想起重复行可以通过添加基数列来消除的方式,而理论上解决了没有唯一密钥的问题,实际上有时是不可能的 – 例如在大规模数据中。 纯粹主义者然后很快build议一个代理人PK,但是从关系理论的angular度来看,一个无意义的代理人可以形成一个关系(表)中的元组(行)的一部分的想法是可笑的。

有几种不同的反对使用NULL。 有些反对意见是基于数据库理论的。 从理论上说,理论与实践没有区别。 在实践中,有。

诚然,一个完全规范化的数据库根本就不存在NULLS。 任何需要省略数据值的地方都是可以排除整行的信息而不会丢失信息的地方。

在实践中,将表分解到这个程度并没有太大的用处,在数据库上执行简单的CRUD操作所需的编程变得更乏味,更容易出错,而不是更less。

有些地方使用NULLS可能会导致问题:本质上这些都围绕着以下问题:缺失数据究竟意味着什么? 所有的NULL真正expression的是没有值存储在一个给定的领域。 但是,应用程序员从缺失数据中得出的推理有时是不正确的,而且会导致很多问题。

由于各种原因,数据可能会从某个位置丢失。 这里有几个:

  1. 数据在这方面不适用。 例如配偶的名字为一个人。

  2. 数据input表单的用户将字段留空,并且该应用程序不需要在该字段中input。

  3. 数据从其他数据库或文件复制到数据库,并且源中缺less数据。

  4. 有一个可选的关系编码在一个外键。

  5. 一个空string存储在Oracle数据库中。

以下是有关何时避免NULLS的一些指导:

如果在正常期望的编程过程中,查询编写者必须编写大量ISNULL,NV,COALESCE或类似的代码,以便用NULL代替有效值。 有时候,最好是在存储时间进行replace,只要存储的是“现实”即可。

如果计数可能会因为包含NULL的行被计数而closures。 通常,只需selectcount(MyField)而不是count(*)就可以避免这种情况。

这里有一个地方,你可以更好地习惯NULLS,并相应地进行编程:每当你开始使用外连接,如LEFT JOIN和RIGHT JOIN。 与外部联接不同的外部联接背后的要点是在缺less一些匹配的数据时获取行。 缺less的数据将作为NULLS给出。

我的底线:不理解它,不要理论。 但是要学会何时离开理论以及如何遵循理论。

对数据字段使用NULL没有任何问题。 将键设置为空时必须小心。 主键不应该是NULL。 外键可以为空,但您必须小心不要创build孤立logging。

如果某些东西是“不存在的”,则应该使用NULL而不是空string或其他types的标志。

而不是写出所有的NULL,三态与布尔逻辑等问题 – 我会提供这个精辟的build议:

  1. 在您的列中不要允许NULL,除非您发现自己添加了一个魔术值来表示丢失或不完整的数据。

  2. 既然你问这个问题,你应该非常小心你如何处理NULL。 它有很多不明显的缺陷。 如有疑问,请不要使用NULL。

我会说,一定要使用空值。 没有其他正确的方法来表示缺乏数据。 例如,使用空string来表示缺less的地址行是错误的,或者使用0来表示缺less的年龄数据项是错误的。 因为空string和0都是数据。 空值是表示这种情况的最好方法。

还有另外一种方法可以使用“N / A”或“N / K”或空string – 一个单独的表格。

例如,如果我们可能会或可能不知道客户的电话号码:

 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)); 

如果我们不知道电话号码,我们只是不添加一行到第二个表。

不要低估你创build一个字段为空的复杂性。 例如,下面的where子句看起来会匹配所有行(位只能是1或0,对不对?)

 where bitfield in (1,0) 

但是,如果位域是无效的,它将错过一些。 或者采取以下查询:

 select * from mytable where id not in (select id from excludetable) 

现在,如果排除表包含null和1,则转换为:

 select * from mytable where id <> NULL and id <> 1 

但是对于id的任何值,“id <> NULL”都是false,所以这将永远不会返回任何行。 这甚至可以通过看到有经验的数据库开发人员。

鉴于大多数人可能会被NULL置之不理,我尽量避免。

这是一个巨大的蠕虫jar,因为NULL可能意味着很多东西:

  • 没有死亡date,因为这个人还活着。
  • 没有手机号码,因为我们不知道它是什么,甚至是否存在。
  • 没有社会安全号码,因为那个人知道没有一个。

其中一些可以通过规范化来避免,其中一些可以通过该列中存在的值(“N / A”)来避免,其中一些可以通过具有单独的列来解释NULL的存在来减轻(“N / K”,“N / A”等)。

这也是一堆蠕虫,因为find它们所需的SQL语法与非空值不同,很难join,而且它们通常不包含在索引条目中。

由于前一个原因,你会发现一个null是不可避免的情况。

由于后一个原因,你应该尽可能地减less它们的数量。

无论如何,总是使用NOT NULL约束来防止需要值的空值。

最好的事情要知道正常forms是他们是指导和指导不应该坚持不懈。 当学术界与现实世界发生冲突时,你很less能find许多幸存的acedemia战士。

这个问题的答案是可以使用空值。 只是评估你的情况,并决定是否希望他们出现在表中,或者如果你觉得你的空值与实际值的比率太高,则将数据折叠到另一个相关的表中。

正如朋友喜欢说的,“不要让完美成为善的敌人”。 想想伏尔泰也是这样说的。 8)

空值的主要问题是它们具有特殊的语义,可以通过比较,聚合和连接产生意想不到的结果。

  • 没有任何东西等于null,并且没有东西不等于,大于或小于null,所以如果要进行批量比较,则必须将nulls设置为占位符值。

  • 这也是可能在连接中使用的复合键的问题。 在自然键包含可空列的情况下,您可能需要考虑使用合成键。

  • 空值可能会丢失,这可能不是你想要的语义。

  • 可以join的列中的空值将消除内部联接中的行。 一般来说,这可能是理想的行为,但它可以为做报告的人提供大象陷阱。

空值还有其他一些细节。 Joe Celko 为Smarties编写的SQL有关于这个主题的整章,是一本好书,值得一读。 空洞是一个很好的解决scheme的地方的一些例子是:

  • join的实体可能存在也可能不存在的可选关系。 空值是在外键列上表示可选关系的唯一方法。

  • 您可能希望使用的列为null,以避免重复计数。

  • 可选的数字(例如货币)值可能存在也可能不存在。 在数字系统中没有有效的占位符值(特别是零是一个合法的值),所以null实际上是唯一的好办法。

一些你可能想要避免使用空值的例子,因为它们很可能引起微妙的错误。

  • 带有FK的代码字段上的“未logging”值与参考表。 使用占位符值,以便在对数据库执行查询时,您(或某个随机的业务分析师在跟踪中)不会无意中将结果集中的行删除。

  • 没有input任何内容的描述字段 – 空string( '' )正常工作。 这节省了不必要的空值作为特殊情况。

  • 报告或数据仓库系统上的可选列。 对于这种情况,请在维度中为“未logging”制作一个占位符行,然后join。 这简化了查询,并与特定的报告工具很好地搭配。

Celko的书再次是对这个问题的一个很好的处理。

根据严格的关系代数,零值是不需要的。 但是对于任何实际的项目,都是需要的。

首先,很多现实世界的数据是未知的或者不适用的,并且空值很好地实现了这种行为。 其次,他们使意见和外部联系更加实际。

你会发现,一步一步的数据采集系统,你不能避免在数据库中的空值,因为提问/数据收集的顺序很less匹配逻辑数据模型。

或者你可以默认值(需要代码来处理这些默认值)。 你可以假设所有的string都是空的而不是null,例如在你的模型中。

或者,您可以暂存用于数据采集的数据库表,直到填充实际数据库表之前获取所有数据为止。 这是很多额外的工作。

对于数据库来说,空值转换为“我没有这个值”。 这意味着(有趣的),允许空值的布尔列是完全可以接受的,并出现在许多数据库模式中。 相比之下,如果你的代码中有一个布尔值,它的值可能是'true','false'或者'undefined',那么你很可能迟早会在​​dailywtf上看到你的代码。

所以是的,如果你需要考虑到一个没有任何价值的领域的可能性,那么允许列上的空值是完全可以接受的。 它比潜在的替代品(空串,零等)

空值可能很难处理,但在某些情况下它们是有意义的。

假设您有一个发票表,其中包含一个具有date值的“PaidDate”列。 在发票付清之前,你在那一栏里写了些什么(假设你事先不知道什么时候能付款)? 它不能是一个空string,因为这不是一个有效的date。 给它一个任意date(如1/1/1900)是没有意义的,因为那个date根本不正确。 看来唯一合理的值是NULL,因为它没有价值。

在数据库中使用空值有一些挑战,但是数据库处理得很好。 真正的问题是,当你从你的数据库加载空值到你的应用程序代码。 那就是我发现事情比较困难的地方。 例如,在.NET中,强types数据集中的date(模仿您的数据库结构)是一个值types,不能为空。 所以你必须build立解决方法。

尽可能避免空值,但不要因为有效用途而排除空值。

我认为你把概念数据build模和物理数据build模混为一谈。

在CDM中,如果一个对象有一个可选的字段,你应该子types的对象,并创build一个新的对象,当该字段不是null。 这就是CDM的理论

在现实世界中,我们为现实世界做出各种妥协。 在现实世界中,NULLS不是很好,它们是必不可less的

我同意上面的许多答案,也相信在适当的情况下,可以在规范化的模式devise中使用NULL,尤其是在您可能希望避免使用某种“幻数”或默认值的情况下误导!

最后,我认为空的使用需要被认真考虑(而不是默认的),以避免在上面的答案中列出的一些假设 ,特别是当NULL可能被假定为“无”或“空”,“未知'或'价值尚未input'。

如果你使用的是Oracle数据库,那么一个问题。 如果你将一个空string保存到一个CHARtypes的列中,那么Oracle将强制该值为NULL而不询问。 所以在Oracle中避免string列中的NULL值可能非常困难。

如果使用NULL值,请学习使用SQL命令COALESCE,特别是string值。 然后,您可以防止传播到您的编程语言的NULL值。 例如,设想一个人有一个名字,中间名和家庭名字,但你想返回一个字段;

  SELECT FullName = COALESCE(FirstName + ' ', '') + COALESCE(MiddleName+ ' ', '') + COALESCE(FamilyName, '') FROM Person 

如果不使用COALESCE,如果任何列包含NULL值,则返回NULL

从技术上讲,关系数据库所依据的关系math中的空值是非法的。 所以从纯技术的语义关系模型的angular度来看,不,他们不好。

在现实世界中,非规范化和对模型的一些违反是可以的。 但是,一般来说,空值是一个指标,您应该更仔细地看待您的总体devise。

我总是非常谨慎的努力,尽可能地将它们归一化。 但这并不意味着他们有时不是最好的select。 但是我肯定会倾向于“没有空值”的一面,除非你真的确信在你的特定基础上拥有更好的空值。

空的岩石。 如果在某些情况下没有必要,那么SQL将不会有作为特殊情况的运算符的IS NULL和IS NOT NULL。 NULL是概念通用的根,其他都不是NULL。 任何时候都可以自由使用NULL,只要数据值可能不存在但不会丢失。 如果所有的时间都是绝对正确的,那么默认值只能补偿NULL。 例如,如果我有一个单比特字段“IsReady”它可能是非常有意义的这个字段有一个默认值为假和NULL是不允许的,但是这隐含地断言,我们知道什么是没有准备好,什么时候实际上我们可能没有这样的知识。 机会是,在工作stream程的情况下,决定准备或不准备的人还没有机会进入他们的意见,所以错误的假设实际上可能是危险的,导致他们忽视似乎有一个决定已经作出,但实际上只是默认。

作为一个中间的例子,我的父亲没有中间名,所以他的中间名字首字母应该是NULL–不是空格,空格或者星号 – 除了中间名字是NMI = No Middle的军队初始。 那有多愚蠢?

虽然在技术上NULL作为一个字段值是可以的,但他们经常皱眉。 根据数据写入数据库的方式,可能(和普通)在字段中以空string值结束,而不是NULL。 因此,任何将此字段作为WHERE子句一部分的查询都需要处理这两个不必要的击键情况。

我对这一天有争议的看法 – 在数据库列中允许NULL的默认设置可能是所有RDBMs土地中最差的公认的devise决策。 每个供应商都这样做,这是错的。 在特定的,具体的,深思熟虑的实例中,NULL是很好的,但是你必须为每一列明确地禁止NULL的想法使得忽略可空性的方式比它应该更普遍。

就个人而言,我认为只有当您使用该字段作为另一个表的外键时,才应该使用空值,以表示该logging不链接到其他表中的任何内容。 除此之外,我发现编程应用程序逻辑时,空值实际上是非常麻烦的。 由于在大多数编程语言中,对于许多数据types,没有直接表示数据库null,所以最终创build了大量的应用程序代码来处理这些空值的含义。 当数据库遇到空整数,并尝试,例如,为它(又名null + 1)添加1的值,数据库将返回null,因为这是如何定义的逻辑。 但是,当编程语言尝试添加null和1时,通常会引发exception。 所以,当代码的值为null时,你的代码就会散乱地检查怎么做,这通常就等同于将数字转换为0,对于文本使用空string,对于date字段则使用一些空date(1900/1/1?) 。

我认为这个问题归结到你解释为NULL的值来表示。 是的,对于NULL值有许多解释,但是其中一些在这里发布的应该永远不会被使用。 NULL的真正含义取决于你的应用程序的上下文,决不应该意味着更多的事情。 例如,有一个build议是,出生date的NULL表示这个人还活着。 这很危险

简而言之,定义NULL并坚持它。 我用它来表示“这个领域的价值在这个时候是未知的”。 这意味着,只有这一点。 如果你需要它意味着别的,那么你需要重新检查你的数据模型。

null表示没有值,而0没有,如果你看到一个0,你不知道的意思,如果你看到一个null,你知道这是一个缺失值

我认为空值更清晰0,因为它们没有清楚地显示存储的值的意图,所以它们是混淆的

不要把我的话讽刺,我的意思是。 除非你使用玩具数据库,否则NULL是不可避免的,在现实世界中我们不能避免使用NULL值。

只是为了说每个人怎么能有名字,中间名,姓。 (中间名和姓氏是可选的,那么在这种情况下,NULL是在那里为你),以及如何可以在博客列表中的每个人有传真,商务电话,办公电话。

NULLS是好的,你必须妥善处理他们的时候检索。 在SQL Server 2008中,有一个稀疏列的概念,你可以避免NULL的空间。

不要将NULL与零和任何其他值混淆。 人们这样说是对的。

谢谢纳文

用null是绝对好的。

这一切归结为正常化与易用性和性能问题。

如果你要坚持完成规范化规则,你最终会写下如下的东西:

从客户c中selectc.id,c.lastname,……..将c.id = cpn.customerid左侧的客户号码cpnjoin到c.id = ca.customerid左侧joincustomeraddress ca。c。在客户端左侧joincustomerphonenumber2 cpn2。 id = cpn2.customerid等等,等等