标准使用“Z”而不是 NULL 来表示缺失数据?

Posted

技术标签:

【中文标题】标准使用“Z”而不是 NULL 来表示缺失数据?【英文标题】:Standard use of 'Z' instead of NULL to represent missing data? 【发布时间】:2011-10-02 01:41:32 【问题描述】:

在是否应该使用 NULL 的争论之外:我负责一个使用 NULL 表示“丢失或从未输入”数据的现有数据库。它与空字符串不同,它的意思是“用户设置了这个值,他们选择了‘空’。”

该项目的另一位承包商坚决支持“NULL 对我来说不存在;我从不使用 NULL,其他人也不应该使用”这一论点。然而,让我感到困惑的是,由于承包商的团队确实承认“丢失/从未输入”和“故意为空或用户指示为未知”之间的区别,他们在整个代码和存储过程中使用单个字符“Z”来在数据库的其余部分中表示“丢失/从未输入”,其含义与 NULL 相同。

尽管我们的共享客户要求对此进行更改,并且我支持此请求,但团队将此视为比我先进得多的 DBA 的“标准做法”;他们不愿意仅根据我无知的请求更改为使用 NULL。那么,任何人都可以帮助我克服我的无知吗? SQL 专家中是否有任何标准,或一小群人,甚至一个大声的声音主张使用“Z”代替 NULL?

更新

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

基本上,我设计数据库时尽可能避免使用 NULL。理由如下:

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

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

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

NULL 处理历来是开发人员在存储过程中犯错误的一个领域。

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

这似乎验证了对这个问题的大部分负面反应。不是应用公认的 6NF 方法来设计 NULL,而是使用特殊值来“尽可能避免 NULL”。我以开放的心态发布了这个问题,我很高兴我了解了更多关于“NULL 有用/NULL 是邪恶”的辩论,但我现在很乐意将“特殊值”方法标记为完全无稽之谈。

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

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

NULL 处理历来是开发人员在存储过程中犯错误的一个领域。

是的,但是成千上万的开发人员已经犯了成千上万次这些错误,避免这些错误的经验教训和注意事项是众所周知的并记录在案。正如这里所提到的:无论您接受还是拒绝 NULL,缺失值的表示都是一个已解决的问题。没有必要仅仅因为开发人员继续犯容易克服(和容易识别)的错误而发明新的解决方案。


作为脚注:我从事 DBE 和开发人员已有 20 多年(这当然足以让我了解数据库工程师和数据库管理员之间的区别)。在我的整个职业生涯中,我一直站在“NULL 是有用的”阵营中,尽管我知道有几个非常聪明的人不同意。我对“特殊价值观”的方法非常怀疑,但对“如何以正确的方式避免 NULL”的学术知识还不够精通,无法站稳脚跟。我总是喜欢学习新事物——20年后我还有很多东西要学。感谢所有为此次讨论做出贡献的人。

【问题讨论】:

NULL 的存在是为了启用 trinary logic,这对于在没有完整信息的情况下保持引用完整性是必要的 - 我会对任何坚决反对他们的公开数据库专家称其完全和彻底的 BS! 从未听说过这种做法。 承包商是否也提出了数字数据的替代NULL? @Andriy:这很容易解决,所有专家都将数字存储在字符字段中并根据需要进行转换(使用 Z 校验!)。哦等等,I'm on the wrong site. 我怀疑有一次,这个承包商试图执行WHERE Column = NULL,并且对他为什么没有得到任何结果感到困惑。 【参考方案1】:

如果域允许缺失值,那么使用 NULL 表示“未定义”是完全可以的(这就是它的用途)。唯一的缺点是必须编写使用数据的代码来检查 NULL。这是我一直这样做的方式。

我从未听说过(或在实践中见过)使用“Z”来表示缺失数据。至于“承包商将此称为 DBA 中的‘标准做法’”,他能否提供一些证据来证明这一说法?正如@Dems 所提到的,您还需要记录“Z”并不意味着“Z”:MiddleInitial 列呢?

像Aaron Alton 和许多其他人一样,我相信 NULL 值是数据库设计的一个组成部分,应该在适当的地方使用。

【讨论】:

我认为这里的关键是“如果域允许缺失值......”在我看来,支持使用 NULL 是有时间和地点的,而支持使用 NULL 是有时间和地点的。避开它们,需要一些智慧才能知道其中的区别。我有时会感觉到,当初级 DBE/DBA 读到这样的警告时,“如果你不考虑它们的行为,NULL 值可能会导致查询和计算中出现意外结果”,他的下意识反应是标记所有 NULL 使用一样糟糕。一旦它成为一种受宗教信仰的观点,它就会在他的整个职业生涯中一直伴随着他。 在 DELETE 或 UPDATE 中忘记 WHERE 子句可能会损害您的数据库 => 永远不要使用它们。要么第一次正确获取数据,要么在编辑器中打开表格并自行完成。 另外,请注意,OUTER 连接会产生 NULL,因此不应使用。同上,卷起来。 Z 在某些标准中用于表示 GMT 时区。 @Erick,这就是不使用 Z 表示“没有价值”的另一个原因。【参考方案2】:

回复承包商 cmets

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

根据此处的大多数其他答案,整个概念存在缺陷

【讨论】:

虽然;据我所知,Oracle 中的空字符串 is NULL。【参考方案3】:

我从未听说过广泛使用'Z' 来替代NULL

(顺便说一句,我不特别喜欢与当面告诉您他们和其他“高级” DBA 比您知识渊博和优秀得多的承包商合作。)

 +=================================+
 |  FavoriteLetters                |
 +=================================+
 |  Person      |  FavoriteLetter  |
 +--------------+------------------+
 |  'Anna'      |  'A'             |
 |  'Bob'       |  'B'             |
 |  'Claire'    |  'C'             |
 |  'Zaphod'    |  'Z'             |
 +---------------------------------+

您的承包商将如何解释最后一行的数据?

大概他会在这张表中选择一个不同的“魔法值”,避免与真实数据'Z'发生冲突?这意味着您必须记住几个魔术值以及哪个在哪里使用......这比只有一个魔术令牌NULL 更好,并且必须记住三值逻辑规则(和陷阱)去用它? NULL 至少是标准化的,不像你的承包商的 'Z'

我也不是特别喜欢NULL,但用一个实际值(或更糟,用几个实际值)替换它到处几乎肯定比NULL更糟糕。 p>

让我在这里重复我的上述评论以提高知名度:如果您想阅读反对NULL的人的严肃和有根据的内容,我会推荐短文"How to handle missing information without using NULLs"(链接到来自@的PDF 987654322@).

【讨论】:

【参考方案4】:

解雇你的承包商。

好的,说真的,这不是标准做法。这可以简单地看出,因为我曾经使用过的所有 RDBMS 都实现了 NULL、NULL 的逻辑、考虑了外键中的 NULL、COUNT 中的 NULL 具有不同的行为等等。

我实际上认为使用“Z”或任何其他占位符会更糟。您仍然需要代码来检查“Z”。但是您还需要记录“Z”并不意味着“Z”,它意味着别的东西。您必须确保阅读此类文档。如果“Z”成为有效数据,会发生什么? (比如首字母的字段?)

在基本层面上,即使不讨论 NULL 与“Z”的有效性,我也会坚持承包商遵守贵公司内部存在的标准做法,而不是他的。在具有替代标准实践的环境中建立他的标准实践会导致混乱、维护开销、误解,并最终增加成本和错误。


编辑

在我看来,在某些情况下使用 NULL 的替代方法是有效的。但只有在这样做会减少代码,而不是创建需要考虑的特殊情况。

例如,我已将其用于日期绑定数据。如果数据在开始日期和结束日期之间有效,则可以通过不包含 NULL 值来简化代码。相反,NULL 开始日期可以替换为“1900 年 1 月 1 日”,NULL 结束日期可以替换为“2079 年 12 月 31 日”。

这仍然会改变预期的行为,因此应谨慎使用:

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

这相当于对抽象进行改造,使所有属性都可以始终具有有效值。它与将特定含义隐式编码为任意选择的值明显不同。

不过,解雇承包商。

【讨论】:

+1 来自我;发现:“我实际上认为使用'Z'或任何其他占位符更糟糕。你仍然需要代码来检查'Z'。但你还需要记录'Z'并不意味着'Z',它意味着别的东西。” 我们需要一个特殊的值——不是NULL,因为NULL是邪恶的——来表示缺失的数据。与所有其他值不同的东西,甚至可能与自身不同(因为两个未知数不能仅仅因为它们是未知的而等同)。某些列显然对这个值没有意义,因此应该禁止它。为方便起见,我们需要特殊的运算符,例如 IS UNKNOWN 或 IS NOT UNKNOWN。 承包商通常会从丰富的经验中得到很好的建议,但仅仅因为有时会发生,并不意味着您必须跟随羊越过推荐的危险悬崖。告诉他们您是数据库的主人和所有者:开发将按照规定进行:遵守或死亡。 如果用户输入Z,那么显然你存储ZZ。如果他们进入 ZZ,你就存储 ZZZ,以此类推。这要求您将所有列都增大一个字符,但这应该不是问题。 +1 从我一般来说 - 但特别是对于使用栅栏发布值作为日期范围(最小日期/最大日期)有意义的编辑,因为它可以节省多少代码 - 特别是如果您必须比较/检查日期范围内的重叠。在这些情况下,min date 表示“一直以来”,max date 表示“直到永远”,这与 NULL 表示“不确定”或“不关心”不同。【参考方案5】:

原则上,正确的数据库设计不需要空值。事实上,有很多数据库设计时不使用空值,并且有很多非常优秀的数据库设计人员和整个开发团队设计数据库时不使用空值。一般来说,在向数据库中添加空值时要谨慎是一件好事,因为它们不可避免地会在以后导致不正确或模棱两可的结果。

我没有听说使用 Z 被称为“标准做法”作为占位符值而不是空值,但我希望您的承包商通常指的是 sentinel 值 的概念,这有时是用于数据库设计。然而,在不使用“虚拟”数据的情况下避免空值的一种更常见和更灵活的方法是简单地将它们设计出来。分解表,以便将每种类型的事实记录在没有“额外”、未指定属性的表中。

【讨论】:

我认为承包商的字面意思是使用“Z”表示“不知道”。 不幸的是,@wallyk 基本上是正确的:这不是学术或理论讨论;由于我自己是开发人员,所以我已经通过代码和存储过程。承包商使用文字字符“Z”表示缺失/未输入的值。 (事实上​​,“未知但已回答”的值即使在当前数据库设计中也永远不会为 NULL;两者都使用空字符串作为文本字段或使用字符“U”作为下拉列表,以表明用户确实回答了问题答案是“我不知道。”) @dportas - 我确实认识到正确的数据库设计不需要使用空值,但是因为我处于“如果你知道如何使用空值,那么有时间和地点可以使用空值”正确,”这个问题的主要目的是了解“NoNULL”阵营中的某个人在良好的数据库设计中使用“Z”是否是标准的、普遍的或任何人都提倡的。【参考方案6】:

即使您以某种方式设法向所有当前和未来的开发人员和 DBA 解释“Z”而不是 NULL,即使他们完美地编写了所有代码,您仍然会混淆优化器,因为它不会知道您已经把这个煮熟了。

使用特殊值表示 NULL(已经是表示 NULL 的特殊值)会导致数据倾斜。例如1900 年 1 月 1 日发生了太多事情,以至于优化器无法理解与您的应用程序真正相关的实际日期范围。

这就像一个经理决定:“打领带不利于生产力,所以我们都要在脖子上戴上胶带。问题解决了。”

【讨论】:

+1 仅用于短语“使用特殊值表示 NULL(这已经是表示 NULL 的特殊值)”。 . . 我认为领结就是这样,用胶带代替的领结被认为更适合这种场合......【参考方案7】:

虽然我从未将“Z”视为表示 null 的神奇值,但我曾看到“X”用于表示尚未填写的字段。也就是说,我只在一个地方见过这种情况,而且我的接口不是数据库,而是 XML 文件……所以我不准备使用这个作为常见做法的论据。

请注意,我们确实必须特别处理“X”,并且正如 Dems 所提到的,我们确实必须记录它,人们对此感到困惑。在我们的辩护中,这是外部供应商强加给我们的,而不是我们自己做的!

【讨论】:

这对于存储复选框选择的数据库来说会非常混乱,这些复选框选择使用 'X' 字符字段检查,而不是检查 ' '(空格)。我希望反物质和物质不要混入同一个数据库...... 我认为这没有得到任何投票,因为它与最初的数据库设计问题没有直接关系,但我至少不得不说,即使是这种“切题”的回应也只是为了强调承包商的方法。 (另外,我认为从现在开始应该将“No Votes”替换为“Z”。) 问题的唯一答案。【参考方案8】:

这很容易成为我听过的最奇怪的意见之一。使用魔法值来表示“无数据”而不是 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 Zzonkas 会和那些没有中间名首字母的人混为一谈吗?或者你的承包商会想出另一个神奇的价值来处理这个问题?

避免使用要求您在数据库已经完全能够处理的代码中实现数据库功能的魔法值。这是一个已解决且易于理解的问题,可能只是您的承包商从未真正理解 NULL 的概念,因此避免使用它。

【讨论】:

以上是关于标准使用“Z”而不是 NULL 来表示缺失数据?的主要内容,如果未能解决你的问题,请参考以下文章

R语言vtreat包自动处理dataframe的缺失值使用分组的中位数来标准化数据列中每个数据的值(和中位数表连接并基于中位数进行数据标化)计算数据列的中位数或者均值并进行数据标准化

SQL 空值

postgresql----数据库表的约束----NOT NULL,DEFAULT,CHECK

使用带有熊猫数据的 CreateDataFrame 时将 NaN 替换为 null

​一文看懂数据清洗:缺失值、异常值和重复值的处理

在两个表上进行一次选择,其中包含 NULL 而不是 null