Tho*_*ger 134 null database-design
我记得读过这篇关于数据库设计的文章,我还记得它说你应该有 NOT NULL 的字段属性。我不记得为什么会这样。
我似乎只能想到,作为应用程序开发人员,您不必测试 NULL和可能不存在的数据值(例如,字符串的空字符串)。
但是对于日期、日期时间和时间 (SQL Server 2008),您会怎么做?你必须使用一些历史或触底的日期。
对此有何想法?
Aar*_*and 250
我认为这个问题措辞不当,因为措辞暗示您已经确定 NULL 是不好的。也许您的意思是“我们应该允许 NULL 吗?”
无论如何,这是我的看法:我认为 NULL 是一件好事。当您仅仅因为“NULL 不好”或“NULL 很难”而开始防止 NULL 时,您就开始编造数据。例如,如果您不知道我的出生日期怎么办?在你知道之前你会在专栏里放什么?如果你和很多反对 NULL 的人一样,你将进入 1900-01-01。现在我将被安置在老年病房,可能会接到当地新闻台的电话,祝贺我长寿,问我长寿的秘诀,等等。
如果可以在您可能不知道列值的情况下输入一行,我认为 NULL 比选择一些任意标记值来表示它是未知的事实更有意义 - 其他人会知道的值必须已经知道,逆向工程,或四处询问以弄清楚它的含义。
不过有一个平衡点——并非数据模型中的每一列都应该可以为空。表单上通常有可选字段,或者在创建行时不会收集的信息片段。但这并不意味着您可以推迟填充所有数据。:-)
此外,使用 NULL 的能力可能会受到现实生活中关键要求的限制。例如,在医学领域,知道为什么值未知可能是生死攸关的问题。心率 NULL 是因为没有脉搏,还是因为我们还没有测量过?在这种情况下,我们是否可以将 NULL 放在心率列中,并在备注或其他列中使用 NULL-因为原因?
不要害怕 NULL,而是愿意学习或规定何时何地应该使用它们,以及何时何地不应使用它们。
big*_*ose 66
成立的原因是:
NULL 不是值,因此没有固有数据类型。当依赖于实际类型的代码也可能收到未类型化的 NULL 时,空值需要在所有地方进行特殊处理。
NULL 破坏了二值(熟悉的 True 或 False)逻辑,需要三值逻辑。这甚至要正确实施要复杂得多,而且大多数 DBA 和几乎所有非 DBA 肯定都不太了解。因此,它会积极地在应用程序中引入许多微妙的错误。
与实际值不同,任何特定 NULL的语义含义都留给应用程序。
像“不适用”、“未知”和“哨兵”这样的语义很常见,还有其他的。它们经常在同一个数据库中同时使用,甚至在同一个关系中;和当然是不明确的、不可区分的和不相容的含义。
它们对于关系数据库不是必需的,如“如何在没有空值的情况下处理缺失信息”中所述。进一步规范化显然是尝试消除 NULL 表的第一步。
这并不意味着永远不允许使用 NULL。它确实认为有很多很好的理由在可行的情况下禁止 NULL。
重要的是,它主张非常努力——通过更好的模式设计、更好的数据库引擎,甚至更好的数据库语言——使更频繁地避免 NULL 变得可行。
Fabian Pascal 在“Nulls Nullified”中回应了许多论点。
Mar*_*ith 33
我不同意,空值是数据库设计的基本要素。正如您所提到的,另一种选择是大量已知值来表示缺失或未知。问题在于 null 被如此广泛地误解并因此被不恰当地使用。
IIRC,Codd 建议可以通过使用两个空标记而不是一个“不存在但适用”和“不存在但不适用”来改进当前的 null(意味着不存在/缺失)的实现。无法想象这种个人会如何改进关系设计。
Nic*_*rre 15
首先让我说我不是 DBA,我是一名开发人员,我根据我们的需要维护和更新我们的数据库。话虽如此,出于几个原因,我有同样的问题。
- 空值使开发更加困难且容易出错。
- 空值使查询、存储过程和视图更加复杂且容易出错。
- 空值占用空间(? 字节基于固定列长度或 2 字节用于可变列长度)。
- 空值可以并且经常影响索引和数学。
我花了很长时间来筛选互联网上的大量回复、评论、文章和建议。不用说,大部分信息与@AaronBertrand 的回复大致相同。这就是为什么我觉得有必要回答这个问题。
首先,我想为所有未来的读者设置一些直接的东西...... NULL 值代表未知数据而不是未使用的数据。如果您有一个包含终止日期字段的员工表。终止日期中的空值是因为它是当前未知的未来必填字段。每个员工无论是活跃的还是终止的,都会在某个时候将日期添加到该字段中。在我看来,这是 Nullable 字段的唯一原因。
也就是说,同一个员工表很可能会保存某种身份验证数据。在企业环境中,员工会被列在 HR 和会计数据库中,但并不总是拥有或需要身份验证详细信息,这在企业环境中很常见。大多数响应会让您相信可以将这些字段置空,或者在某些情况下为它们创建一个帐户,但永远不要向它们发送凭据。前者会导致您的开发团队编写代码来检查 NULL 并相应地处理它们,而后者会带来巨大的安全风险!系统中从未使用过的帐户只会增加黑客可能的访问点的数量,而且它们会为从未使用过的东西占用宝贵的数据库空间。
鉴于上述信息,处理将要使用的可空数据的最佳方法是允许可空值。这是可悲但真实的,您的开发人员会因此而讨厌您。第二种类型的可为空的数据应该放在一个相关的表中(IE:帐户、凭据等)并且具有一对一的关系。这允许用户在没有凭据的情况下存在,除非需要它们。这消除了额外的安全风险、宝贵的数据库空间,并提供了一个更干净的数据库。
下面是一个非常简单的表结构,显示了所需的可为空列和一对一关系。

我知道我参加聚会有点晚了,因为这个问题是多年前被问到的,但希望这将有助于阐明这个问题以及如何最好地处理它。
Tho*_*ser 13
除了 NULL 使开发人员感到困惑的所有问题之外,NULL 还有另一个非常严重的缺点:性能
从性能的角度来看,可以为 NULL 的列是一场灾难。以整数算术为例。在没有 NULL 的理智世界中,使用 SIMD 指令在数据库引擎代码中矢量化整数算术是“容易”的,以比每个 CPU 周期快 1 行的速度执行几乎任何计算。但是,在您引入 NULL 的那一刻,您需要处理 NULL 创建的所有特殊情况。现代 CPU 指令集(阅读:x86/x64/ARM 和 GPU 逻辑)根本无法有效地执行此操作。
以除法为例。在非常高的层次上,这是使用非空整数所需的逻辑:
if (b == 0)
do something when dividing by error
else
return a / b
Run Code Online (Sandbox Code Playgroud)
使用NULL,这变得有点棘手。与b您一起将需要一个指标 ifb为 null 和类似的 for a。支票现在变成:
if (b_null_bit == NULL)
return NULL
else if (b == 0)
do something when dividing by error
else if (a_null_bit == NULL)
return NULL
else
return a / b
Run Code Online (Sandbox Code Playgroud)
NULL 算术在现代 CPU 上的运行速度明显慢于非空算术(大约 2-3 倍)。
当您引入 SIMD 时,情况会变得更糟。使用 SIMD,现代 Intel CPU 可以在一条指令中执行 4 x 32 位整数除法,如下所示:
x_vector = a_vector / b_vector
if (fetestexception(FE_DIVBYZERO))
do something when dividing by zero
return x_vector;
Run Code Online (Sandbox Code Playgroud)
现在,也有一些方法可以在 SIMD 域中处理 NULL,但这需要使用更多的向量和 CPU 寄存器并进行一些巧妙的位掩码。即使有很好的技巧,即使是相对简单的表达式,NULL 整数算法的性能损失也会慢 5-10 倍。
上面类似的东西适用于聚合,在某种程度上,也适用于连接。
换句话说:SQL 中 NULL 的存在是数据库理论与现代计算机实际设计之间的阻抗不匹配。NULL 使开发人员感到困惑有一个很好的理由——因为在大多数理智的编程语言中,整数不能为 NULL——这不是计算机的工作方式。
Der*_*ney 10
维基百科关于 SQL Null 的文章有一些关于 NULL 值的有趣评论,并且作为一个与数据库无关的答案,只要您意识到为特定 RDBMS 设置 NULL 值的潜在影响,它们在您的设计中是可以接受的。如果不是,您将无法将列指定为可为空。
请注意您的 RDBMS 如何在 SELECT 操作(如数学)以及索引中处理它们。
Chr*_*ers 10
有趣的问题。
我似乎只能想到,作为应用程序开发人员,您不必测试 NULL 和可能不存在的数据值(例如,字符串的空字符串)。
它比那更复杂。Null 有许多不同的含义,并且不允许在许多列中使用 null 的一个非常重要的原因是,当该列为 null 时,这意味着一件事并且只有一件事(即它没有出现在外连接中)。此外,它允许您设置数据输入的最低标准,这非常有用。
但是对于日期、日期时间和时间 (SQL Server 2008),您会怎么做?你必须使用一些历史或触底的日期。
这立即说明了空值的问题,即存储在表中的值可能意味着“此值不适用”或“我们不知道”。对于字符串,空字符串可以用作“这不适用”,但对于日期和时间,则没有这样的约定,因为没有常规意义上的有效值。通常在那里你会被困在使用 NULL 的地方。
有一些方法可以解决这个问题(通过添加更多关系和连接),但这些方法会带来与数据库中的 NULL 完全相同的语义清晰度问题。对于这些数据库,我不会担心这一点。对此你真的无能为力。
编辑:一个领域是空值是不可缺少的是外键。在这里,它们通常只有一种含义,与外连接含义中的 null 相同。这当然是问题的一个例外。