关系数据库中的空值是否正常?

时间:2008-10-02 16:58:47

标签: database-design oop null

有一种观点认为不应该在关系数据库中允许空值。也就是说,表的属性(列)不应该允许空值。来自软件开发背景,我真的不明白这一点。似乎如果null在属性的上下文中有效,那么应该允许它。这在Java中非常常见,其中对象引用通常为null。没有广泛的数据库经验,我想知道我是否在这里遗漏了一些东西。

33 个答案:

答案 0 :(得分:67)

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

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

在某些情况下,有一个空字段是有用的,特别是当你想出于性能原因而想要避免另一个连接时(尽管如果数据库引擎设置正确,这应该不是问题,除非在特殊的高性能方案中。)

- 亚当

答案 1 :(得分:38)

反对null的一个论点是它们没有明确定义的解释。如果字段为null,则可以将其解释为以下任何一种:

  • 值为“Nothing”或“Empty set”
  • 没有任何价值对该领域有意义。
  • 价值未知。
  • 尚未输入该值。
  • 该值为空字符串(对于不区分空值和空字符串的数据库)。
  • 某些特定于应用程序的含义(例如,“如果值为null,则使用默认值。”)
  • 发生错误,导致该字段具有空值,但实际上不应该。

某些架构设计人员要求所有值和数据类型都应该有明确定义的解释,因此空值很差。

答案 2 :(得分:32)

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

答案 3 :(得分:27)

取决于。

只要你理解为什么你在数据库中允许NULL需要按列进行选择)以及你将如何解释,忽略或否则处理他们,他们没事。

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

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

因此,最终,问题必须通过惯例,文档和一致性来解决。

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

在许多属性未知的情况下,为每个列加入另一个表是没有意义的,这样可以在更简单的设计中允许NULL。连接的开销,主键的空间要求在现实世界中毫无意义。

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

答案 4 :(得分:17)

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

完全规范化的数据库确实可以在没有NULLS的情况下相处。任何需要遗漏数据值的地方都是可以省去整行而不会丢失信息的地方。

实际上,将表分解到这个程度并没有什么用处,并且对数据库执行简单CRUD操作所需的编程变得更加繁琐且容易出错,而不是更少。

有些地方使用NULLS会导致问题:基本上这些问题围绕着以下问题:缺失数据究竟意味着什么?真正传达的所有NULL都是在给定字段中没有存储的值。但推理应用程序员从缺失的数据中抽取有时是不正确的,这会导致很多问题。

由于各种原因,某个位置可能会丢失数据。以下是一些:

  1. 在此上下文中,数据不适用。例如配偶一个人的名字。

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

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

  4. 在外键中存在可选关系。

  5. 空字符串存储在Oracle数据库中。

  6. 以下是关于何时避免NULLS的一些指导原则:

    如果在正常预期编程过程中,查询编写者必须编写大量ISNULL,NV,COALESCE或类似代码,以便将有效值替换为NULL。有时候,如果存储的是“现实”,最好在商店时间进行替换。

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

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

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

答案 5 :(得分:16)

对数据字段使用NULL没有任何问题。将键设置为null时必须小心。主键永远不应为NULL。外键可以为null,但您必须注意不要创建孤立记录。

如果某些内容“不存在”,那么您应该使用NULL而不是空字符串或其他类型的标记。

答案 6 :(得分:11)

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

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

  2. 由于您提出了这个问题,因此您应该非常小心处理NULL。它有很多非常明显的缺陷。如有疑问,请勿使用NULL。

答案 7 :(得分:9)

还有另一种方法可以使用“N / A”或“N / K”或空字符串 - 一个单独的表格。

E.g。如果我们可能知道或不知道客户的电话号码:

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

如果我们不知道电话号码,我们就不会在第二个表格中添加一行。

答案 8 :(得分:8)

我会说绝对应该使用Null。没有其他正确的方法来表示缺乏数据。例如,使用空字符串表示缺少的地址行是错误的,或者使用0表示缺少的年龄数据项是错误的。因为空字符串和0都是数据。 Null是表示这种情况的最佳方式。

答案 9 :(得分:7)

不要通过使字段为NULL来低估您创建的复杂性。例如,以下where子句看起来会匹配所有行(位只能是1或0,对吗?)

where bitfield in (1,0)

但是如果位域是NULLable,它将会遗漏一些。或者采取以下查询:

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

现在,如果excludetable包含null和1,则转换为:

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

但对于id的任何值,“id&lt;&gt; NULL”为false,因此这将永远不会返回任何行。这使得即使是经验丰富的数据库开发人员也会感到惊讶。

鉴于大多数人都可以被NULL看守,我会尽量避免使用它。

答案 10 :(得分:6)

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

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

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

它也是一种蠕虫,因为找到它们所需的SQL语法与非空值的SQL语法不同,很难加入它们,并且它们通常不包含在索引条目中。

由于前一个原因,您将找到无法避免null的情况。

由于后一种原因,您仍应尽力减少它们的数量。

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

答案 11 :(得分:6)

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

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

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

  • Nulls可能会失去计数,这可能不是您想要的语义。

  • 您可以加入的列中的空值将消除内部联接中的行。一般来说,这可能是期望的行为,但它可以为进行报告的人提供大象陷阱。

还有很多其他的微妙之处。 Joe Celko的SQL for Smarties有一整章关于这个主题,是一本好书,无论如何都值得一读。空值是一个很好的解决方案的地方的一些例子是:

  • 加入实体可能存在或不存在的可选关系。 Null是在外键列上表示可选关系的唯一方法。

  • 您可能希望使用null来删除计数的列。

  • 可能存在或不存在的可选数字(例如货币)值。在数字系统中没有“未记录”的有效占位符值(特别是在零是合法值的情况下),因此null实际上是唯一的好方法。

您可能希望避免使用空值的地方的一些示例,因为它们可能会导致细微的错误。

  • 在代码字段上的'Not Recorded'值,对参考表使用FK。使用占位符值,以便在对数据库进行查询时,您(或某些随机业务分析师在轨道上)不会无意中从结果集中删除行。

  • 没有输入任何内容的描述字段 - 空字符串('')可以正常工作。这节省了必须将空值视为特殊情况。

  • 报表或数据仓库系统上的可选列。对于这种情况,在维度中为“未记录”创建一个占位符行,并加入该行。这简化了查询,并且可以很好地使用临时报告工具。

同样,Celko的书是对这个主题的一个很好的对待。

答案 12 :(得分:5)

关于普通形式的最好的事情是它们是指南和指南不应该被坚持遵守。当学术界与现实世界发生冲突时,你很少找到许多幸存的乙炔战士。

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

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

答案 13 :(得分:4)

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

首先,许多真实世界的数据是未知的或不适用的,并且空值很好地实现了这种行为。其次,它们使视图和外连接更加实用。

答案 14 :(得分:3)

Null可能难以使用,但在某些情况下它们是有意义的。

假设您有一张发票表,其中包含“PaidDate”列,其中包含日期值。在支付发票之前你在该栏中放了什么(假设你事先不知道什么时候会支付)?它不能是空字符串,因为那不是有效日期。给它一个任意的日期(例如1/1/1900)是没有意义的,因为那个日期根本不正确。似乎唯一合理的值是NULL,因为它没有值。

在数据库中使用空值有一些挑战,但数据库可以很好地处理它们。真正的问题是当您将数据库中的空值加载到应用程序代码中时。这就是我发现事情变得更加困难的地方。例如,在.NET中,强类型数据集中的日期(模仿数据库结构)是值类型,不能为null。所以你必须建立变通方法。

尽可能避免使用空值,但不要因为它们具有有效用途而将其排除在外。

答案 15 :(得分:3)

对于数据库,null转换为“我没有这个值”。这意味着(有趣的是),允许空值的布尔列是完全可以接受的,并且出现在许多数据库模式中。相反,如果您的代码中有一个布尔值,其值可能为'true','false'或'undefined',那么您很可能迟早会看到您的代码在thedailywtf上结束:)

所以是的,如果你需要允许字段根本没有任何值,那么在列上允许空值是完全可以接受的。它明显优于潜在的替代品(空字符串,零等)

答案 16 :(得分:3)

我认为您将概念数据建模与物理数据建模混淆。

在CDM中,如果对象具有可选字段,则应该对该对象进行子类型化,并为该字段不为空时创建新对象。这就是CDM中的理论

在物质世界中,我们为现实世界做出各种妥协。在现实世界中,NULLS非常好,它们是必不可少的

答案 17 :(得分:3)

您会发现逐步数据采集系统无法避免在数据库中出现空值,因为提问/数据采集的顺序很少与逻辑数据模型匹配。

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

或者,您可以拥有用于数据采集的临时数据库表,直到填充实际数据库表之前获取所有数据。这是一项额外的工作。

答案 18 :(得分:3)

我同意上面的许多答案,并且相信在适当的情况下,可以在规范化的架构设计中使用NULL - 特别是在您可能希望避免使用某种“幻数”或默认值的情况下,转,可能会误导!

最终,我认为null的使用需要经过深思熟虑(而不是默认),以避免上面答案中列出的一些歧视,特别是在假设假设的情况下'没有'或'空','未知'或'价值尚未输入'。

答案 19 :(得分:2)

如果您使用的是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

答案 20 :(得分:2)

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

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

NULLS很好,你必须在检索时正确处理它们。在SQL Server 2008中,有一个稀疏列的概念,您可以避免使用NULL空间。

不要将NULL与零和任何其他值混淆。无论如何,人们都这样做是正确的。

由于 纳温

答案 21 :(得分:2)

null表示没有值,而0表示没有,如果你看到0你不知道其含义,如果你看到一个null你知道它是一个缺失的值

我认为空值更清晰,0和''令人困惑,因为它们没有清楚地显示存储值的意图

答案 22 :(得分:2)

虽然技术上NULL可以作为字段值,但它们经常被不满。根据数据如何写入数据库,可能(并且常见)在字段中以空字符串值结束而不是NULL。因此,任何将此字段作为WHERE子句的一部分的查询都需要处理两种不必要的击键方案。

答案 23 :(得分:2)

我当天有争议的观点 - 在数据库列中允许NULL的默认值可能是所有RDBM域中最普遍接受的设计决策。每个供应商都这样做,这是错误的。在某些特定的,深思熟虑的实例中,NULL很好,但是你必须明确地禁止每列的NULL,这使得疏忽的可空性方式比它应该更常见。

答案 24 :(得分:2)

NULL摇滚。如果在某些情况下没有必要,SQL就不会有IS NULL和IS NOT NULL作为特例操作符。 NULL是概念通用的根,其他都是NOT NULL。只要有可能数据值不存在但没有错过,就可以自由地使用NULL。如果它们始终绝对正确,则默认值只能补偿NULL。例如,如果我有一个单比特字段“IsReady”,那么这个字段的默认值为false并且不允许NULL可能是完全合理的,但这隐含地断言我们知道什么都没有准备好,实际上我们可能没有这样的知识。在工作流场景中,有可能决定准备就绪的人还没有机会进入他们的意见,所以默认为false实际上可能是危险的,导致他们忽略了一个似乎有的决定已经制作但实际上只是默认。

作为旁边,并且参考中间的初始例子,我的父亲没有中间名,因此他的中间名首字母将是NULL - 不是空白,空格或星号 - 除了他的中间首字母是NMI的军队=没有中间初始。这有多愚蠢?

答案 25 :(得分:2)

从技术上讲,空值在关系数据库所基于的关系数学中是非法的。因此,从纯技术,语义关系模型的角度来看,不,它们并不合适。

在现实世界中,非规范化和一些违反模型的行为都可以。但是,一般来说,空值表示您应该更仔细地查看整体设计。

我总是非常警惕null,并尽可能地将它们标准化。但这并不意味着它们有时不是最好的选择。但我绝对会倾向于“无效”,除非你确定在你的特定基础上使用空值更好。

答案 26 :(得分:1)

我认为问题归结为你解释NULL的值来表示。是的,对于NULL值有很多解释,但是这里发布的一些不应该被使用。 NULL的真正含义取决于您的应用程序的上下文,绝不应该意味着多于一件事。例如,一个建议是,出生日期字段上的NULL表示该人还活着。这很危险。

简而言之,定义NULL并坚持下去。我用它来表示“此时此区域的值未知”。这意味着,仅此而已。如果你需要它来表达其他东西,那么你需要重新检查你的数据模型。

答案 27 :(得分:1)

就我个人而言,我认为只有在将字段用作另一个表的外键时才能使用空值,以表示此记录不链接到另一个表中的任何内容。除此之外,我发现在编写应用程序逻辑时,null值实际上非常麻烦。因为对于许多数据类型,在大多数编程语言中没有直接表示数据库null,所以最终会创建大量应用程序代码来处理这些空值的含义。当数据库遇到空整数,并尝试向其添加值1(也就是空值+ 1)时,数据库将返回null,因为这是逻辑的定义方式。但是,当编程语言尝试添加null和1时,它通常会抛出异常。所以,你的代码最终会乱码,当值为null时检查该做什么,这通常只等于数字转换为0,文本为空字符串,日期字段为空日期(1900/1/1?)

答案 28 :(得分:0)

这一切都取决于规范化与易用性和性能问题。

如果你要坚持完成规范化规则,你最终会写出看起来像这样的东西:

从客户c中选择c.id,c.lastname,....... 在c.id = cpn.customerid上加入customerphonenumber cpn 在c.id = ca.customerid上左连接customeraddress ca. 在c.id = cpn2.customerid上左连接customerphonenumber2 cpn2 等,等等

答案 29 :(得分:0)

  

如果null在属性的上下文中有效,那么应该允许它。

但null 是什么意思?这就是问题所在。这是“没有价值”,但有十几个不同的原因可能没有价值,而“null”并没有给你任何线索在这种情况下它意味着什么。 (尚未设置,不适用于此实例,不适用于此类型,未知,不可知,未找到,错误,程序错误,......)

  

这在Java中非常常见,其中对象引用通常为空。

有一种思想流派说null references there are bad there, too。同样的问题:null 是什么意思

IIRC,Java既有“null”又有“未初始化”(虽然后者没有语法)。因此,Gosling意识到对每种“无价值”使用“null”的愚蠢。但为什么要停留在just two

答案 30 :(得分:0)

使用null绝对没问题。

答案 31 :(得分:-1)

相关问题:How do I enforce data integrity rules in my database?

我最初从很多小桌子开始,几乎没有nullalbe字段。然后我了解了LINQ to SQL IsDiscriminator属性,LINQ to SQL仅支持单表继承。因此,我将其重新设计为具有大量nullalbe字段的单个表格。

答案 32 :(得分:-3)

作为一名拥有30年经验的分析师/程序员,我只会说应该取消NULL并摆脱痛苦。

-1,01/01/0001/12/319999和?所有这些都足够了,没有思维扭曲代码来处理这些令人讨厌的NULL。