SQL:空字串与NULL值


72

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

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

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

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

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

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


11
您是否知道在Oracle中,空字符串 NULL?
user281377 2010年

8
@ammoQ:Oracle对零长度字符串的处理是非标准的。此外,''即使在Oracle中,也不相同NULL。例如,为CHAR(1)列分配值''将导致' '(即空格)而不是NULL。此外,如果Jacek使用的是Oracle,那么这个问题甚至可能不会出现:-)
Dean Harding 2010年

2
Dean:关于char(1)示例,您是正确的,但这又是另一个WTF,因为'' IS NULLtrue在PL / SQL中求值。
user281377 2010年

“我是否正确地认为使用空字符串作为未知值会导致数据库对事实“撒谎”?” 如果您的企业用户不在乎未知与空虚,那么谎言甚至有关系吗?
安迪

如果您必须遵循使用字符串的路线...,请确保它为空。为了所有开发人员的利益,请勿让带有空格的字符串代表您的未知值。我求求你。
Airn5475

Answers:


83

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

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

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


5
完全同意。NULL是有原因的。在您的电子邮件地址为[NOT]的情况下选择COUNT(*)是NULL,而不是比较慢的字符串比较(即使对于我想是空字符串,但我不确定这一点:)。
LudoMC 2010年

5
我认为NULL这并不意味着没有电子邮件地址,我认为这意味着该电子邮件地址目前未知,不存在或者由于其他原因无法填写。幸运的是,在任何情况下都不会希望将确实没有或不打算拥有任何电子邮件地址的人的信息保留在数据库中,否则可能需要一个单独的布尔字段。
阿列克谢(Alexey)2012年

9
@Alexey-NULL表示没有值。正如其他人指出的那样,空字符串是一个值。
Ramhound 2012年

3
@Ramhound,我同意空字符串是一个值,而NULL隐约意味着“没有值”。我只是解释了我对“无价值”的解释。我认为,这与“该人尚未打开任何电子邮件帐户”不同。而是“没有为该人记录电子邮件地址”。
阿列克谢2012年

5
@Ramhound NULL表示没有值。没有中间名的人在那里没有价值。因此,在中间的初始列中也应使用NULL ...这与该答案中提出的参数完全相反。
Izkata 2012年

41

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

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

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


4
+1这是针对开发人员反对空字符串的“最少惊讶”参数。以后再也没有开发人员会期望使用空字符串来表示“没有电子邮件地址”。
托马斯

6

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

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

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

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

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

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


7
-1。数据库是否在驱动网站无关紧要。设计数据库与网页设计的世界不同。数据库的设计应独立于用于写入数据库的接口来捕获有关业务领域的事实。按照您的逻辑,如果同时第一个应用程序是可执行文件,则应使用null吗?如果第一个应用程序是Web应用程序,而下一个应用程序是移动应用程序,会发生什么?设计数据库以使用规范化规则捕获事实,并设计要写入的网站。
托马斯

我很高兴您学习了如何在此站点上编写和评论:)我仍然相信DB应该支持使用它的应用程序。检查我编辑的答案。
康斯坦丁·彼得鲁赫诺夫

4
没有使用数据库的应用程序,数据库就无法生存。以我的经验,这根本不是真的,也是短视的。数据库几乎总是在为其设计应用程序之外使用。通常,数据库的生存期比为其构建应用程序的生存期更长。应该设计数据库来收集有关业务的事实,并且应该构建UI来读取和写入数据库,而不是相反。关系设计与应用程序设计完全不同。
托马斯

2
数据库并非原始应用程序使用的示例:报告,与其他系统的集成。
Thomas

1
正如Thomas所指出的,DB可以并且经常被多个应用程序使用,这增加了保持DB数据整洁的想法的重要性。如果您不希望/无法在应用程序中处理NULL,则可以在数据访问层将它们替换为“魔术值”(很好的描述Thomas)。这样,将来任何想要访问数据库的应用程序都无需了解/符合原始应用程序的魔术值。
bentemes 2012年

5

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


2
+1-这就是我所说的“神奇价值解决方案”。我们必须为每种数据类型设计一个魔术值,以表示缺少值。另外,在某些列中,公共魔术值是或变为合法值,因此需要新的魔术值。
托马斯

5

不幸的是,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。


4

使用空值。

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

如果您想查找具有电子邮件地址的用户,那么哪个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


3

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

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

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


即使具有世界上最好的意图,验证也不能解决这个问题-他可能仍然必须使用一种处理行的方法,其中必须为所有列提供某种值。在那种情况下,问题将依然存在-没有价值时使用什么价值?答案当然是:表明没有价值的价值。在数据库中,通常为NULL。
jmoreno

2

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

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

但是,我不建议您使用此字段,而是使用单独的字段询问是否知道不存在任何字段。


1

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

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


1

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

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

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

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

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

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


我隐约记得在某处读过,使用NULL实际上是数据库的一项成本(在计算和存储方面)。提出该公式的好处很重要。
Jacek Prucia,2011年

不要忘记一VARCHAR列将至少占用1个字节来存储字符串的长度,即使它为零。
dan04 2011年

空单元格和null都是标记错误的方法。不对。空值是一种指示缺少值的方法。我敢打赌大多数RDBMS在每一行上使用一个位数组来指示哪些列为空。因此,额外的空间是如此之小以至于无关紧要。担心额外的处理是过早的优化,与为其他开发人员“发现”您故意使用空字符串而创建的减速带相比,它毫无用处。
Thomas

3
没有空值。这是鸵鸟的方法。“我们将把自己的头埋在沙子里,并声明不存在缺失的值”。这通常会导致产生“魔术值解决方案”,在该解决方案中,您必须为每种数据类型提供一个魔术值以表示缺少值。
托马斯

1

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

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

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

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

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


为什么email字段不能不允许为空且字符串长度为零 -简单地说:因为任何对数据库一无所知的开发人员都不会期望空字符串具有魔术的含义。您试图使自己的魔术值代表每个数据库中已经基本存在的值:一种表示不存在值的概念。为什么要重新发明轮子?而且,NULLS的想法离老派还很遥远。空是理解关系数据库设计的基石。
托马斯

大声笑。就像我从程序员的角度说过的那样,空值几乎总是使人痛苦不堪,对于Business LOGIC来说几乎不需要。作为开发人员,我个人并不关心关系设计。如果我做到了,我将是一名DB兄弟。如果我从数据库中得到一个空值,我几乎总是将其转换为有理数,例如一个空字符串,然后让我光荣的OOP设计做到这一点很神奇。该框架负责解决DBA对世界施加的那些愚蠢的无效行为。我知道DB帅哥必须处理它,我对您有帮助。但是,作为程序员,我不必这样做。我有更好的解决方案。
ElGringoGrande 2012年

您“从不”必须处理空值。因此,您所描述的是鸵鸟解决方案与神奇价值解决方案的结合。“我将忽略缺少值的事实,并将所有空整数都转换为-1”。直到有一天-1是真实值。应当指出,MS向.NET添加泛型的原因之一是为了解决数据库和应用程序代码之间的巨大阻抗不匹配,并且主要围绕在中间层代码中表达空值。这些“愚蠢的空值”也存在于业务逻辑中。
Thomas

db中不存在某些整数(或为null)的事实并不意味着我必须用-1来表示它或evanable nullable(int)。如果您认为这是处理null的唯一方法,那么您就不会很好地理解编程。请记住,null就是一无所有。就像您说的那样,null表示某种数据结构中缺少值的占位符。这意味着什么。业务逻辑很少(这与从不一样)很少需要此概念,因为它与行为有关,而不是数据。而且当它为null时,很少是代表这一点的最佳方法。
ElGringoGrande,2012年

即使是业务逻辑,也必须考虑(含义代表)缺失的值,根据我的经验,在过去20年来我所见过或构建的几乎每个系统中,都是如此。该数据库正在对要捕获和存储的业务事实进行建模。如果业务逻辑希望能够与数据库交互,则它必须知道如何处理空值。无论是自定义结构,魔术值还是泛型都无关紧要。业务逻辑需要具有处理从数据库接收到的缺失值的能力,以及将值标记为对数据库缺失的能力。
托马斯

-1

我认为这没什么大不了,但是当NULL存在时,我会更喜欢它。

当我查看表中显示的数据时(例如SQL Server Management Studio中的数据),如果缺失值显示为NULL并且背景颜色不同,则可以更好地区分缺失值。

如果看到空白,我总是想知道它是否真的是空的,或者是否有空白或一些不可见的字符。使用NULL可以保证一眼就空了。

在此处输入图片说明

我通常不区分应用程序中的值,因为这是意外和奇怪的,NULL和空字符串表示不同的含义。在大多数时候,我采取防御性方法,只与这两个州打交道。但是对于我个人来说,查看数据时更容易处理NULL。


这似乎并没有提供任何实质性的过度点进行,而且在以往12个回答解释
蚊蚋

@gnat:我不同意,答案中没有人提到人类查看数据的方面。只有一个NULL值,但是可以有很多看起来像一个空字符串的值(不仅是空格,而且还有很多奇怪的Unicode字符)。我看不到任何其他提及此问题的答案。
汤姆·帕索雷克(TomPažourek),2016年

据我所知,这是在5年前发布的第二个最佳答案中列出的:“对于任何看数据库的程序员来说都是显而易见的……”等
咬到了

@gnat:我明白你的意思,尽管我认为作者的意思不同。我相信他更多地是关于NULL表示可选字段,但是空字符串也可以用于必填字段,因此NULL对于缺少值更合乎逻辑。我同意他的观点。但是我的答案指出了一个事实,即空字符串并不像NULL值那样明确,因为许多事情乍一看可能看起来像空字符串,而实际上并不是空字符串。
汤姆·帕索里克(TomPažourek)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.