50

我很欣赏数据库表中 NULL 值的语义含义,它不同于 false 和空字符串 ''。但是,我经常阅读有关字段可为空时的性能问题,并建议在 NULL 实际上语义正确的情况下使用空字符串。

什么情况下适合使用可空字段和空值?有哪些取舍?简单地完全避免使用 NULL 并简单地使用空字符串、false 或 0 来表示不存在值是否明智?

更新

好的 - 我了解 '' 和 NULL 之间的语义差异以及 NULL 是适当字段值的(与性能无关的)情况。但是,让我扩展一下暗示的性能问题。这是来自 Schwartz、Zeitsev 等人的优秀“高性能 MySQL” http://www.borders.co.uk/book/high-performance-mysql-optimization-backups-replication-and-more/857673/

MySQL 更难优化引用可空列的查询,因为它们使索引、索引统计和值比较更加复杂。可为空的列使用更多的存储空间,并且需要在 MySQL 内部进行特殊处理。当一个可为空的列被索引时,每个条目需要一个额外的字节,甚至可以导致固定大小的索引(例如单个整数列上的索引)在 MyISAM 中转换为可变大小的索引。

更多内容: Google 图书预览

这很可能是确定的答案——我只是在寻找一线的第二意见和经验。

4

11 回答 11

37

但是,我经常阅读有关字段可为空时的性能问题,并建议在 NULL 实际上语义正确的情况下使用空字符串。

我将暂时对单词选择挑剔:

  • 即使它是一个重要的性能因素,但这并不意味着使用值而不是 NULL在语义上是正确的。在 SQL 中,NULL 具有语义作用,表示缺失或不适用的值。给定 RDBMS 实现中 NULL 的性能特征与此无关。性能可能因品牌或版本而异,但语言中NULL的目的是一致的。

无论如何,我没有听说过任何证据表明 NULL 表现不佳。我会对任何显示可空列性能比不可空列差的性能测量参考感兴趣。

我并不是说我没有错,或者在某些情况下它不可能是真的——只是做空洞的假设没有意义。科学不是由猜想组成的;必须通过可重复的测量来证明证据。

指标还告诉您性能差异有多大,因此您可以判断这是否值得担心。也就是说,影响可能是可测量的且非零,但与更大的性能因素(例如正确索引表或调整数据库缓存大小)相比仍然微不足道。

在 MySQL 中,搜索 NULL 可以从索引中受益:

mysql> CREATE TABLE foo (
  i INT NOT NULL,
  j INT DEFAULT NULL,
  PRIMARY KEY (i),
  UNIQUE KEY j_index (j)
);

mysql> INSERT INTO foo (i, j) VALUES 
  (1, 1), (2, 2), (3, NULL), (4, NULL), (5, 5);

mysql> EXPLAIN SELECT * FROM foo WHERE i = 3;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | foo   | const | PRIMARY       | PRIMARY | 4       | const |    1 |       | 
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

mysql> EXPLAIN SELECT * FROM foo WHERE j IS NULL;
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | foo   | ref  | j_index       | j_index | 5       | const |    2 | Using where | 
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+

请注意,这仍然不是性能的衡量标准。我只展示了您可以在搜索 NULL 时使用索引。我要断言(诚然没有测量,但这只是 StackOverflow),索引的好处掩盖了在搜索 NULL 与空字符串时任何可能的惩罚。

选择零或空白或任何其他值来代替 NULL 不是正确的设计决定。您可能需要在列中使用这些值作为重要值。这就是为什么 NULL 存在的原因,作为一个定义在任何数据类型的值域之外的值,因此您可以使用整数或字符串或其他任何值的全部范围,并且仍然有一些东西来表示“没有上述值。 "

于 2009-01-23T02:25:40.853 回答
12

MySQL 手册实际上有一篇关于NULL 问题的好文章。

希望能帮助到你。

还发现了关于 NULL 和 Performance 的其他SO 帖子

于 2009-01-23T00:09:41.093 回答
5

我们不允许在我们的数据库中使用 NULL 值,除非它用于数值或日期。我们这样做的原因是因为数值有时不应该默认为零,因为这非常非常糟糕。我是股票经纪人的开发人员, NULL0之间有很大的区别。如果我们确实想将默认值恢复为零,即使我们不这样存储它们,使用 COALESCE 也会派上用场。

MyVal = COALESCE(TheData, 0)

当我们从平面文件中批量插入数据时,我们使用格式文件来确定数据的条目,无论如何都会自动将空值转换为空白字符串。

日期默认为取决于我认为的排序规则可能出现的任何值,但我们默认为 1900 之类的值,而且日期非常重要。其他纯文本值并不那么重要,如果留空通常符合条件。

于 2009-01-27T13:32:17.733 回答
3

通常,如果需要属性,则将其定义为 Not NULL,如果可以省略,则将其定义为可为空。

于 2009-01-23T00:08:42.250 回答
3

不应使用空字符串代替NULL. NULL代表什么都没有,因为空字符串是什么,里面什么都没有。与另一个值(偶数)NULL比较时总是为假,并且不会在函数中求和。NULLNULLCOUNT

如果您需要表示未知信息,则无法替代NULL.

于 2009-01-23T00:20:47.657 回答
3

正如@ForYourOwnGood 所说 - Null 应该用于“未知”信息。例如:如果您有很多字段需要客户在注册时填写,其中一些是可选的。出于某种原因,您可能希望为该特定客户保留一个 ID,并且由于您不知道可选字段是否是客户真正选择的留空,您应该在第一次保存时将它们设置为 NULL 即“未知”行。如果客户提交了表单,通过了您的所有验证,然后您保存了信息,那么您就知道该可选字段是有意留空的。

这只是使用 NULL 的一个很好的例子。

于 2013-06-30T20:57:55.263 回答
2

NULL 列的含义或多或少是“不适用于此上下文”。我通常在两种情况下使用 NULL 列:

  • 如果该字段不适用(假设您有一个布尔列 is_thirsty 并添加了两个数据集。一个人和一块石头。如果是人,则将 is_thirsty 设置为 true 或 false,而对于石头,您可能会将其设置为 NULL。
  • 如果我需要标记某些内容并使用该值存储一些数据。就像库存关闭日期一样,您可以使用它来 a) 指定不能再更改库存和 b) 指定库存关闭的时间。如果库存集仍然可以更改,我只是创建了 closed_at 列并将其设置为 NULL ,而不是有两列(closed_at和),但在它关闭后设置日期。is_closed

基本上它归结为这样一个事实,即当字段的空性具有与空字段不同的唯一语义时,我使用 NULL。没有中间首字母就是这样。没有结束日期意味着库存集仍然可以更改。

NULL 值可能会产生令人讨厌的副作用,它们会使您难以将数据添加到表中,而且通常情况下,您最终会得到 NULL 值和空字符串的混杂。

此外,NULL 不等于任何东西,如果你不小心的话,它会在所有地方搞砸查询。

就个人而言,我仅在上述两种情况之一适用时才使用 NULL 列。当空值除了没有值之外没有任何意义时,我从不使用它来表示空字段。

于 2009-01-23T00:17:06.977 回答
2

我知道有时 MySQL 的 NULL 语义是完全合适的。

也就是说,它们确实会妨碍您,尤其是在文本字段中。

这是一个真实世界的例子。

我们希望将数据从 FileMaker 数据库复制到 mysql 表中。

如果我们执行“SELECT * from table where textfield <> 'test'”,则不会返回文本字段为 NULL 的行。这很可能不是您所期望或想要的。

如果在 where 查询中使用可为空的字段,无论是单独使用还是作为 AND 的一部分,除非使用 IS NULL 测试,否则永远不会返回为空的条目。我们必须做一些类似“where ((textfield<>"test") OR (textfield IS NOT NULL))”的事情,这充其量是丑陋的。

所以在这种情况下,我们可能不希望该字段可以为空。

这里的问题是您不能使用 Filemaker 将空字符串插入 MySQL。它被转换为 NULL,如果您使列不为空,则会出错!如果您确实允许 NULL,那么到 mysql 的传输工作,但是您的 not 查询无法按照您想要的方式运行!

解决方法是更改​​表两次,将导入后的现有空值转换为空字符串,然后将表更改回再次允许空值。你!

该死的文件制作者。

于 2018-07-06T06:19:37.893 回答
1

当然,主要的好处是你提到的 NULL 的语义含义。

除此之外——它可能取决于你的存储引擎,一如既往,检查文档——但至少在某些数据库中,NULL 占用的空间比常规值小得多。例如,如果您有一个声明为 20 个字符的“varchar”列,并且很少填充,则可以通过将其设置为 NULL 而不是空字符串来节省大量磁盘空间。

我从来没有听说过使用 NULL 的任何性能问题,相反。我听说有人搞砸了他们的计数,因为他们计算了 NULL 错误,但从来没有计算过性能。如果这是真的,我很想听听!

于 2009-01-23T00:15:48.087 回答
0

如今,任何自重的数据库引擎都不会因为正确使用 NULL 而受到惩罚,除非您的查询设计不正确(这通常不是您经常遇到的关于 NULL 的问题)。

您应该首先注意按预期使用数据库(包括 NULL);然后担心何时以及是否发生优化后果。

在 SQL 复杂性和准确性方面,不正确的 NULL 列值的累积影响几乎肯定会超过愚弄 Mother DBMS 的好处。此外,它会弄乱你的头脑,以及以后试图弄清楚你在做什么的任何人的头脑。

于 2009-01-23T00:18:24.480 回答
0

在 Oracle 等一些数据库上,MySQL 上的某些想法可能是真的:

  • 空值没有被索引,那么如果寻找空值可能是瓶颈。
  • 行上的尾随空值可以节省空间。
于 2009-01-23T00:22:06.467 回答