使用“Z”代替NULL表示缺失数据的标准用法?

76

除了是否应该使用NULL的讨论之外:我负责管理一个现有的数据库,它使用NULL来表示“缺失或从未输入”的数据。这与空字符串不同,空字符串表示“用户设置了此值,并选择了‘空’”。

项目中的另一位承包商坚定地站在“对我来说不存在NULL;我从不使用NULL,其他人也不应该使用NULL”的立场上。然而,令我困惑的是,由于承包商的团队确实承认“缺失/从未输入”和“故意为空或由用户标记为未知”的区别,他们在代码和存储过程中使用单个字符“Z”来表示“缺失/从未输入”,其含义与数据库中的NULL完全相同。

尽管我们共同的客户要求更改此设置,并且我支持此请求,但团队将其视为DBA远比我先进的“标准做法”;他们不愿根据我无知的要求单独更改以使用NULL。 那么,有人能帮助我消除我的无知吗?SQL专家中是否存在任何标准、小群体或甚至单个响亮的声音主张使用“Z”代替NULL?

更新

我收到了承包商的回复。当客户要求删除特殊值以允许在没有数据的列中使用NULL时,以下是他说的话:

基本上,我设计了数据库以尽可能避免使用NULL。这是我的理由:

字符串[VARCHAR]字段中的NULL从未必要,因为空(零长度)字符串提供完全相同的信息。

整数字段(例如ID值)中的NULL可以通过使用在数据中永远不会出现的值(例如,对于整数IDENTITY字段,使用-1)来处理。

日期字段中的NULL可能会导致日期计算中的复杂问题。例如,在计算日期差异(例如[RecoveryDate]和[OnsetDate]之间的天数差异)等计算逻辑时,如果一个或两个日期为空,则逻辑将崩溃--除非明确允许两个日期都为空。这需要额外的工作和处理。如果使用“默认”或“占位符”日期作为[RecoveryDate]和[OnsetDate](例如,“1/1/1900”),则数学计算可能显示“异常”值--但是日期逻辑不会崩溃。

NULL处理传统上是存储过程开发人员犯错误的领域。

在我作为DBA的15年中,我发现尽可能避免NULL是最好的做法。

这似乎证实了对这个问题的大多数负面反应。与其采用接受的6NF方法来设计排除NULL,我们使用特殊值来“尽可能避免NULL”。我以开放的心态发布了这个问题,我很高兴了解了关于“NULL有用/NULL有害”辩论的更多信息,但现在我已经非常自信地将“特殊值”方法标记为完全无意义。

空(零长度)字符串提供完全相同的信息。

不,它不是;在我们正在修改的现有数据库中,NULL表示“从未输入”,而空字符串表示“输入为空”。

NULL处理传统上是存储过程开发人员犯错误的领域。

是的,但那些错误已经被成千上万的开发人员犯过数千次,避免那些错误的教训和警告已知并记录下来。正如在这里提到的那样:无论您接受还是拒绝NULL,缺失值的表示是一个“已解决问题”。没有必要发明新的解决方案,只因为开发人员继续犯易于克服(易于识别)的错误。


作为一则脚注:我作为一个DBE和开发人员已经有20多年的经验(这足以让我分清数据库工程师和数据库管理员之间的区别)。在我的职业生涯中,我一直坚持“NULL值也是有用的”观点,尽管我知道有几个非常聪明的人持不同意见。我对“特殊值”方法非常怀疑,但是对于如何正确地避免NULL的学术问题并不够熟练,无法做出明确的立场。我总是喜欢学习新东西——即使在20年后我还有很多要学习的。感谢所有为使这次讨论成为有用之事做出贡献的人。

60
NULL存在的目的是为了启用三值逻辑,这在缺乏完整信息的情况下维护引用完整性时非常必要。对于任何坚决反对这种逻辑的自称数据库专家,我会毫不客气地质疑他们的能力! - gordy
17
完全没有听说过这种做法。 - Calvin Allen
14
承包商是否已经提出了数值数据的代理NULL? - Andriy M
14
@Andriy: 这很容易解决,所有专家都将数字存储在字符字段中,并根据需要进行转换(带有Z-checks!)。哦,等等,我在错误的网站上。(http://thedailywtf.com/) - mu is too short
12
我怀疑这个承包商曾经尝试执行 WHERE Column = NULL,并且对于为什么没有得到任何结果感到困惑。请注意,NULL不应该用等于号(=)进行比较,而应该使用IS NULL或IS NOT NULL。 - Mike Caron
显示剩余17条评论
8个回答

105

解雇你的承包商。

好的,说真的,这不是标准做法。我曾经使用过的所有关系型数据库管理系统都实现了NULL,对NULL进行了逻辑处理,在外键中考虑了NULL,在COUNT函数中对NULL有不同的行为等等。

我认为使用“Z”或任何其他占位符更糟糕。你仍然需要编写代码来检查“Z”。但你还需要记录,“Z”并不意味着“Z”,而是代表其他东西。你必须确保这样的文档被阅读。如果“Z”成为有效数据的一部分会发生什么?(例如一个initial的字段?)

即使不讨论NULL和“Z”的有效性,从基本层面上讲,我也坚持承包商应当遵循公司内存在的标准做法,而不是他自己的标准做法。在一个存在另一种标准做法的环境中采用他的标准做法会导致混乱、维护开销、误解,最终会增加成本和错误。


编辑

在我看来,有些情况下使用非NULL的替代方案是合理的。但只有在这样做可以减少代码而不是创建需要考虑的特殊情况时才可以这样做。

例如,我曾经在日期边界数据中使用过这种方法。如果数据在开始日期和结束日期之间是有效的,那么可以通过不使用NULL值来简化代码。相反,可以将NULL开始日期替换为“1900年1月1日”,将NULL结束日期替换为“2079年12月31日”。

这仍然可能改变预期行为,因此应该谨慎使用:

  • WHERE end-date IS NULL 不再提供仍然有效的数据
  • 你刚刚创建了自己的千年虫
  • 等等。

这相当于重构抽象化,使得所有属性始终可以具有有效值。这与隐式编码特定含义到任意选择的值中截然不同。

然而,还是要解雇承包商。


21
我认为使用“Z”或任何其他占位符都更糟糕。你仍然需要编写代码来检查“Z”。但你还需要记录“Z”并不意味着“Z”,它意味着其他东西。+1,完全正确。 - Mitch Wheat
21
我们需要的是一个特殊的值 - 不是NULL,因为NULL是有害的 - 来表示缺失数据。这个值应该与其他所有值不同,甚至可能与自身不同(因为两个未知数不能简单地等同)。对于某些列来说,显然不适合使用这个值,因此应该禁止使用。为了方便起见,我们需要特殊的运算符,如IS UNKNOWN或IS NOT UNKNOWN。 - Mike Caron
5
承包商通常有着深厚的经验并提供有价值的建议,但只是因为这种情况偶尔出现,并不意味着你必须跟随大众前往那个被推荐的危险悬崖。告诉他们你是数据库的主人和所有者:开发将按规定进行,遵守或死亡。 - wallyk
2
如果用户输入Z,那么显然你要存储ZZ。如果他们输入ZZ,则存储ZZZ,以此类推。这需要你将所有列都增加一个字符的大小,但这不应该是问题。 - Chas. Owens
2
总体来说我给你点赞,特别是在编辑方面。使用栅栏值来表示日期范围(最小日期/最大日期)可以节省很多代码,尤其是当你需要比较/检查日期范围重叠的情况下。在这些情况下,最小日期表示“从一直以来”,而最大日期表示“直到永远”,这与NULL表示“不确定”或“不关心”是不同的。 - Joel Brown
显示剩余3条评论

26

这绝对是我听过的最奇怪的观点之一。使用一个魔数代表“无数据”,而不是使用NULL,就意味着你所有的代码都必须进行后处理来处理/丢弃“无数据”/“Z”值。

NULL之所以特殊,是因为数据库在查询中处理它的方式。例如,看看下面这两个简单的查询:

select * from mytable where name = 'bob';
select * from mytable where name != 'bob';

如果name为空(NULL),它将不会出现在第一个查询结果中,更重要的是,它也不会出现在第二个查询结果中。 NULL仅与显式搜索NULL匹配,例如:

select * from mytable where name is NULL;

如果数据中可能有Z作为有效值,会发生什么情况呢?比如说你要存储某个人的中间名首字母?那么名为Zachary Z Zonkas的人会被归为没有中间名的人吗?还是你的承包商会想出另一个特殊值来处理这种情况呢?

避免使用神奇值,因为数据库已经完全能够处理的功能,却需要在代码中实现数据库特性。这是一个已经解决而且深入理解的问题,也许只是你的承包商从未真正掌握NULL的概念,因此避免使用它。


22
如果域允许缺失值,那么使用NULL表示“未定义”是完全可以的(这就是它存在的原因)。唯一的缺点是消费数据的代码必须编写以检查NULL。这是我一直以来的做法。
我从未听说过(或在实践中见过)使用“Z”表示缺失数据。至于“承包商引用DBA中的标准惯例”,他能否提供一些证据支持这种说法?正如@Dems所提到的,您还需要记录“Z”不表示“Z”的情况:例如MiddleInitial列怎么办?
Aaron Alton和许多其他人一样,我认为NULL值是数据库设计的一个重要组成部分,并且应该在适当的地方使用。

3
我认为这里的关键是“如果域允许缺失值……”。在我的看法中,有时候应该倡导使用NULL,有时候则不必使用它们,需要一些智慧去分辨。我有时候感觉到,当初级数据库工程师/管理员读到“如果你没有考虑NULL的行为,它们可能会导致查询和计算结果出现意外情况”的警告时,他们的膝跳反应就是将所有NULL用法都标记为不好。一旦形成了宗教般的信念,它就会伴随他度过职业生涯。 - Boris Nikolaevich
1
忘记在DELETE或UPDATE语句中加入WHERE子句可能会对数据库造成伤害 => 永远不要这样做。要么在第一次获取数据时就确保正确性,要么打开编辑器自己手动处理表格。 - MatBailie
另外,请注意,OUTER连接会产生NULL值,因此不应使用。同样,ROLL UP也是如此。 - MatBailie
3
在一些标准中,Z被用来表示格林威治标准时间时区。 - Erick Robertson
2
@Erick,这是不使用Z表示“无值”的又一个原因。 - Boris Nikolaevich

17

即使您设法向所有当前和未来的开发人员和数据库管理员解释“Z”代替NULL,即使他们编写的所有代码都是完美的,您仍将使优化器混淆,因为它不知道您已经弄出这个东西。

使用特殊值代表NULL(本来就是用特殊值表示NULL),将导致数据发生偏差。例如,许多事件发生在1900年1月1日,这将使优化器无法理解真正与您的应用程序相关的日期范围。

这就像一个经理决定:“系领带会影响生产力,所以我们所有人都要用胶带贴在脖子上。问题解决了。”


10
+1 只是因为这个短语“使用一个特殊的值来表示NULL(这已经是用于表示NULL的特殊值)”…… - Mike Sherrill 'Cat Recall'
我认为蝴蝶结就是那样的,一条用胶带代替的领带,被认为更适合这个场合... - Soren

10
我从未听说过广泛使用 'Z' 作为 NULL 的替代品。
(顺便说一句,我不想特别与一个告诉你他们和其他“高级”数据库管理员比你更有知识和更优秀的承包商合作。)
 +=================================+
 |  FavoriteLetters                |
 +=================================+
 |  Person      |  FavoriteLetter  |
 +--------------+------------------+
 |  'Anna'      |  'A'             |
 |  'Bob'       |  'B'             |
 |  'Claire'    |  'C'             |
 |  'Zaphod'    |  'Z'             |
 +---------------------------------+

你的承包商会如何解释最后一行的数据?

可能他会在这个表格中选择一个不同的“魔法值”,以避免与真实数据'Z'碰撞?这意味着你必须记住几个魔法值,还要记住它们在哪里使用...这比只有一个魔法标记NULL并且必须记住三值逻辑规则(和陷阱)更好吗?与你的承包商的'Z'不同,NULL至少是标准化的。

我也不特别喜欢NULL,但是在每个地方毫无头绪地用实际值(或更糟的是用几个实际值)替换它,几乎肯定比NULL更糟糕。

让我在这里重复我的上面的评论,以便更好地看到:如果你想读一些反对NULL的认真和有根据的东西,我建议阅读"如何处理缺失信息而不使用NULL"(链接到The Third Manifesto homepage 的PDF文件)。


5
原则上,正确的数据库设计并不需要使用null。事实上,有很多数据库是没有使用null设计的,也有很多非常出色的数据库设计师和整个开发团队都是在不使用null的情况下设计数据库的。一般来说,对于向数据库添加null值要谨慎,因为它们不可避免地会导致后续产生不正确或模棱两可的结果。
我从未听说过使用Z作为占位符值而不是null被称为“标准做法”,但我想你的承包商可能是指通常在数据库设计中使用的“哨兵值”概念。然而,一种更常见且灵活的避免使用“虚拟”数据而不使用null的方法是将其设计掉。将表分解,使每种类型的事实记录在一个不具有“额外”未指定属性的表中。

1
我认为承包商字面上意思是使用“Z”表示“不知道”。 - wallyk
不幸的是,@wallyk基本上是正确的:这不是一个学术或理论讨论;由于我自己也是开发人员,我已经查看了代码和存储过程。承包商正在使用字面字符“Z”表示缺失/未输入的值。(实际上,“未知但已回答”的值即使在当前数据库设计中也从不为空;对于文本字段,两者都使用空字符串,对于下拉列表,则使用字符“U”表示用户回答了问题并且回答是“我不知道”。) - Boris Nikolaevich
1
@dportas - 我确实认识到正确的数据库设计不需要使用空值,但由于我属于“如果你知道如何正确使用NULL,那么在某些情况下使用NULL是有时机和地点的”,所以这个问题的主要目的是了解在“无NULL”阵营中,是否有人将'Z'用于良好的数据库设计,且该做法是否标准、常见或被任何人推崇。 - Boris Nikolaevich

3

回复承包商的评论

  • 空字符串 <> NULL
  • 空字符串需要2个字节的存储空间+偏移读取
  • NULL使用null位图=更快
  • IDENTITY并不总是从1开始(为什么要浪费一半的范围?)

整个概念都有缺陷,与其他大多数答案一样。


4
虽然;就我所记得的而言,在 Oracle 中,空字符串 NULL。 - MatBailie

1

虽然我从未见过“Z”作为表示null的魔法值,但我见过使用“X”来表示未填写字段的情况。尽管如此,我只在一个地方看到过这种情况,而我的接口不是数据库,而是一个XML文件...所以我不能准备用这个作为普遍做法的论据。

请注意,我们必须特别处理“X”,正如Dems所提到的,我们必须对其进行文档记录,而且人们可能会感到困惑。在我们的辩护中,这是由外部供应商强制施加给我们的,而不是我们自己想出来的东西!


这对于使用字符字段“X”表示选中,空格表示未选中的复选框选择存储的数据库来说非常令人困惑。我希望反物质和物质不会混入同一个数据库中... - wallyk
我认为这个回答没有得到任何投票,因为它与原始的数据库设计问题没有直接关联,但是我至少要说,即使这个“离题”的回答只是强调了承包商方法的荒谬性。(另外,我认为从现在开始,“无投票”应该用“Z”来代替。) - Boris Nikolaevich
这个问题的唯一答案。 - Pindatjuh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接