it-swarm.cn

SQL:空字串与NULL值

我知道这个话题有点争议,互联网上有很多各种各样的文章/观点。不幸的是,他们中的大多数人都假设该人不知道NULL和空字符串之间的区别是什么。因此,他们通过联接/聚集讲述令人惊讶的结果的故事,并且通常会做一些更高级的SQL课程。这样,他们绝对会遗漏所有要点,因此对我毫无用处。因此,希望这个问题和所有答案将使话题向前发展。

假设我有一个包含个人信息(姓名,出生等)的表,其中的一列是具有varchar类型的电子邮件地址。我们假设由于某些原因,某些人可能不想提供电子邮件地址。将此类数据(不带电子邮件)插入表时,有两个可用的选择:将单元格设置为NULL或将其设置为空字符串('')。让我们假设我知道选择一个解决方案而不是另一个解决方案的所有技术含义,并且可以为任何一种情况创建正确的SQL查询。问题是,即使两个值在技术层面上都不同,但在逻辑层面上却完全相同。看完NULL和”之后,我得出一个结论:我不知道那个人的电子邮件地址。同样,无论我多么努力,我都无法使用NULL或空字符串发送电子邮件,因此显然大多数SMTP服务器都同意我的逻辑。所以我倾向于在不知道该值的地方使用NULL,并认为空字符串是一件坏事。

经过与同事的激烈讨论,我提出了两个问题:

  1. 我是否假设使用空字符串作为未知值会导致数据库对事实“撒谎”,对吗?更准确地说:使用SQL关于什么是价值和什么不是价值的想法,我可能得出结论:我们拥有电子邮件地址,只是发现它不为空。但是后来,当尝试发送电子邮件时,我会得出一个矛盾的结论:不,我们没有电子邮件地址,@!#$数据库一定在撒谎!

  2. 是否有逻辑上的情况,其中空字符串”可以很好地承载重要信息(除了值和没有值),而通过其他任何方式(如附加列)进行存储都会很麻烦/效率低下。我见过很多帖子声称有时将空字符串与实数值和NULL一起使用是很好的,但是到目前为止,还没有看到一种合乎逻辑的方案(就SQL/DB设计而言)。

附言有些人会试图回答,这只是个人品味的问题。我不同意对我来说,这是一个具有重大后果的设计决策。因此,我希望看到一些出于逻辑和/或技术原因而对此表示反对的答案。

73
Jacek Prucia

我会说NULL是“没有电子邮件地址”的正确选择。有多个“无效”电子邮件地址,而“”(空字符串)只是一个。例如,“ foo”不是有效的电子邮件地址,“ a @ b @ c”无效,依此类推。因此,仅因为“”不是有效的电子邮件地址就没有理由将其用作“无电子邮件地址”值。

我认为您说“”不是“我对此栏没有价值”的正确方法是正确的。 “” is一个值。

一个示例,其中“”可能是有效值,与NULL分开的可能是一个人的中间名。并非每个人都有中间名,因此您需要区分“无中间名”(“-空字符串”)和“我不知道此人是否有中间名”(NULL )。可能还有许多其他示例,其中空字符串仍然是列的有效值。

84
Dean Harding

在同意上述意见的同时,我将此观点作为主要动机:

  1. 对于查看数据库的任何程序员来说,显而易见的是,标记为NULL的字段是Optional字段。 (即该记录不需要该列的数据)
  2. 如果将字段标记为“非空”,则任何程序员都应直观地认为它是必填字段。
  3. 在允许空值的字段中,程序员应该期望看到空值而不是空字符串。

为了进行自我记录的直观编码,请使用NULL而不是空字符串。

41
colinbashbash

在您的示例中,如果它是直接来自Web字段的值-我将使用空字符串。如果用户可以选择指定他不想提供电子邮件,或者可以删除它-则为NULL。

以下是您可以考虑的要点的链接: https://stackoverflow.com/questions/405909/null-vs-empty-when-dealing-with-user-input/405945#405945

---编辑(回复托马斯的评论)---

没有使用数据库的应用程序,数据库就无法生存。如果应用程序不能正确使用NULL或'',则没有价值。

考虑一个示例,在该示例中,用户填写LONG表单并按Enter,它将向服务器发送持久请求。他可能正在输入电子邮件。很可能您想将他的所有内容存储在电子邮件字段中,以便稍后他可以完成它。如果他只输入一个字符怎么办?如果他输入一个字符然后将其删除怎么办?当不需要电子邮件时,有时用户希望将其删除:这是清除字段的最简单方法。同样,如果不需要电子邮件,则值得在发送之前对其进行验证。

另一个示例:用户以spamto @ [bigcompany] .com的身份提供电子邮件-在这种情况下,即使存在并且有效(甚至可能存在),也无需发送电子邮件。发送这样的邮件可能很便宜,但是如果有1万个用户使用此类电子邮件进行日常订阅,那么这种验证可以节省大量时间。

6
Konstantin Petrukhnov

使用空值。

仅将表中的字段设置为可空时,就没有必要存储值”了。它也使查询更加明显。

如果您想查找具有电子邮件地址的用户,那么哪个SQL查询更为明显和可读?

  1. SELECT * FROM Users WHERE email_address != ''

  2. SELECT * FROM Users WHERE email_address IS NOT NULL

  3. SELECT * FROM Users WHERE email_address != '' and email_address IS NOT NULL

我会说2是。尽管在存储错误数据的情况下3更可靠。

对于表格上的电子邮件地址(是可选的),它也应反映在表格中。在SQL中,它是一个可为空的字段,这意味着它是未知的。

除了简单的不良设计之外,我认为在表中存储空字符串没有任何合理的商业价值。这就像存储一个字符串值'NULL'或'BLANK'并让开发人员假设它为null或空字符串。对我来说,这是不好的设计。为什么在存在NULL时将其存储?

只需使用NULL,您就会使每个人都更加快乐。

更多信息:

SQL使用三值逻辑系统:True,False和Unknown。

为了获得更好,更详细的解释,我建议开发人员阅读: SQL查询-超越TRUE和FALSE

5
spong

不幸的是,Oracle将长度为零的VARCHAR字符串表示形式与NULL表示形式相混淆。它们在内部都由一个零值的字节表示。这使得讨论变得更加困难。

围绕NULL的许多困惑都集中在三值逻辑周围。考虑以下伪代码:

if ZIPCODE = NULL
    print "ZIPCODE is NULL"
else if ZIPCODE <> NULL
    print "ZIPCODE is not NULL"
else print "Something unknown has happened"

您不会期望收到第三条消息,但这就是您在三种有价值的逻辑下会得到的。三种有价值的逻辑引导人们发现许多错误。

混乱的另一个来源是从缺乏数据中得出推论,例如从夜间没有吠叫的狗中推论出结论。通常,这些推论不是NULL编写者打算传达的内容。

话虽如此,在很多情况下,NULL可以很好地处理缺少数据的情况,并可以准确地产生所需的结果。一个示例是可选关系中的外键。如果使用NULL指示给定行中没有关系,则该行将退出内部联接,正如您所期望的那样。

另外,请注意,即使您完全避免在存储的数据中使用NULLS(第六种普通形式),如果执行任何外部联接,您仍将不得不应对NULLS。

5
Walter Mitty

我认为Dean Hardings的回答很好地涵盖了这一点。话虽如此,我想提到在数据库级别谈论NULL与空字符串时,您应该考虑一下其他数据类型。如果没有提供日期,您会存储最小日期吗?或-1(如果没有提供int)?当没有值时存储值意味着您必须跟踪整个非值范围。每种数据类型至少有一个(可能会在实际值为-1的情况下获得更多,因此您需要一些替代方法,等等)。如果您需要/想要在应用程序级别上做一些“前卫的事情”,那是一回事,但是他们不需要污染您的数据。

5
bendemes

对于特定的技术问题,问题不是null还是空字符串,而是验证失败。空字符串不是有效的电子邮件地址!

对于哲学问题,答案是相似的:验证您的输入。如果空字符串是所讨论字段的有效值,则应对其进行编码。如果不是,请使用null。

空字符串将是回答以下问题的有效输入:哑剧对长颈鹿说了什么?

3
Steven A. Lowe

我想到一个具有NULL和空字符串的原因:

  • 您有有效的电子邮件地址:[email protected]
  • 您没有任何人(可能应该问一个):NULL
  • 您知道此人没有电子邮件地址:Empty String.

但是,我不建议这样做,并使用单独的字段询问是否知道不存在任何字段。

2
Marcel

据我了解,问题是应该选择NULL和空字符串的解释。这取决于特定字段可以包含多少个状态

解释取决于如何访问数据库。如果代码中存在一层可以完全抽象出数据库的层,那么选择可行的任何策略(包括两个库)都是完全可以接受的。 (不过,清楚地记录该政策很重要)。但是,如果要在多个地方访问数据库,则应该使用非常简单的方案,因为代码将更难以维护,并且在这种情况下可能是错误的。

1
apoorv020

好吧,基本上在逻辑层面上,“无效”值和“无用户输入”之间没有区别,大多数情况下它们只是“特殊情况”。错误情况。

具有null会占用额外的空间:ceil(columns_with_null/8)以字节为单位/每行。

空单元格和null都是标记错误的两种方式,应该默认。为什么需要2个“错误”状态?如果NULL占用额外的空间,并且含义与空字符串完全相同,为什么还要使用NULL?当您有两件事(可能意味着)完全相同时,这只会引入混乱和冗余,很容易忘记您应该使用NULL而不是空字符串(例如,如果用户省略了某些字段)。

而且您的数据可能变得一团糟。在一个完美的世界中,您会说“数据将始终是正确的,我会记住的” ...但是,当人们必须在团队中工作并且并非每个人都在您的水平上时,看到WHERE的情况并不少见(aa。 xx <>''AND bb.zz IS不为空)

因此,我不必每隔一天纠正我的团队成员,而是执行简单的规则。没有空值,永远!

计算NON-NULL值的速度更快...一个简单的问题是,您需要做什么呢?

1
Slawek

我倾向于不是从数据库的角度来看它,而是从程序的角度来看它。我知道这个问题是针对SQL单击的,但实际上,有多少用户不再直接访问数据?

在程序中,我不喜欢null/nothing。有一些例外,但仅此而已。这些异常实际上只是不好的实现。

因此,如果用户未输入电子邮件,则应该确定该内容是否有效。如果可以使用空白电子邮件,则显示空白字符串。如果用户未放入电子邮件并且违反了规则,则对象应指出这一点。

具有意义的空值的想法是古老的,是现代程序员必须解决的问题。

即使在数据库设计中,为什么email字段也不能不允许null和长度为零的字符串,而另一个字段则指示用户是否输入了某些内容? DBMS有那么多要求吗?在我看来,数据库既不应该处理业务逻辑也不应该处理显示逻辑。它不是为此而构建的,因此在处理它方面做得很差。

1
ElGringoGrande