MySQL,插入NULL还是空字符串更好?

242

我在网站上有一个表单,其中包含许多不同的字段。其中一些字段是可选的,而其他字段是强制性的。在我的数据库中,有一个表格保存了所有这些值,那么当用户没有输入任何数据时,将NULL值或空字符串插入到数据库列中哪个更好呢?

6个回答

226

通过使用 NULL,您可以区分“不放置数据”和“放置空数据”。

还有一些其他的差异:

  • NULL 的长度为 NULL,空字符串的长度为 0

  • NULL 在空字符串之前排序。

  • COUNT(message) 会计算空字符串但不会计算 NULL

  • 您可以使用绑定变量搜索空字符串,但不能搜索 NULL。这个查询:

    SELECT  *
    FROM    mytable 
    WHERE   mytext = ?
    

    无论您从客户端传递什么值,都无法匹配mytext中的NULL。要匹配NULL,您必须使用其他查询:

    SELECT  *
    FROM    mytable 
    WHERE   mytext IS NULL
    

6
你认为哪个更快?0、NULL还是""? - Atul Dravid
10
在InnoDB中,NULL值占据的空间较少。 - Timo Huovinen
44
我认为这是一个可以的答案,但它完全忽略了问题中的“最佳实践”要素,只关注了与问题相关的事实(NULL排序顺序和长度?这些都不重要)。对于大多数文本数据输入类型,“无响应”和“空响应”之间没有区别,因此我认为这是一个很好的问题,值得一个更好的答案。 - Nick
6
当设置唯一字段时,NULL也非常有效。例如,如果您有一个类似于“驾驶执照”的字段,用于添加人的驾驶执照号码,但是某些人没有驾驶执照。由于这是一个唯一字段,第一个没有驾驶执照号码的人将被添加,但是下一个人将会引发唯一约束错误而无法添加。因此,使用NULL更好。 - Saifur Rahman Mohsin
1
@Quassnoi 啊,抱歉...我的意思是,为什么将驾驶证号设置为唯一的不好呢...? - cedbeu
显示剩余6条评论

46

需要考虑的一件事是,如果你曾经打算更换数据库,那么 Oracle不支持空字符串。它们会自动转换为NULL,并且您无法使用诸如WHERE somefield = ''这样的子句查询它们。


12
这对我来说听起来非常可疑,即使通过你的链接,所以我试了一下。空字段设为'',Oracle会忽略它。报告长度为null而不是0。这太错误了。肯定有某种方法可以解决这个问题。我想我会把这个作为另一个问题发表。 - Steve B.
1
Steve B.:请查看这个问题:https://dev59.com/nHM_5IYBdhLWcg3w6HzT - Quassnoi
谢谢您提供的参考,但我仍然不理解其原因。发布在 https://dev59.com/KXM_5IYBdhLWcg3wslbs。 - Steve B.
可能值得更新答案,以包括Quassnoi发布的链接中的信息。 - SamuelKDavis
1
@Steve B:这是Oracle很久以前做出的设计决策。他们无法改变这种行为,否则将破坏大量基于此行为设计的遗留Oracle应用程序。因此,Oracle将永远被困在这个设计决策中(即零长度字符串等同于NULL)。 - spencer7593
8
Peoplesoft(使用Oracle数据库)使用一个空格表示空值。非常愚蠢。他们还使用0.00025来表示FTE的0,因为不允许使用0。该产品中做出了令人愉悦的选择。 - JP Duffy

10
最好在MySQL数据库中插入NULL以保持一致性。外键可以存储为NULL,但不能存储为空字符串。
如果在限制条件中使用了空字符串,则会出现问题。您可能需要插入一个带有唯一空字符串的假记录来满足外键约束条件。我猜这是不好的做法。
另请参阅:外键可以为NULL和/或重复吗?

约束问题曾经让我遇到过困难,这就是为什么我对这个答案点赞的原因。 - HPWD
但是如果您使用NULL,请务必确保不会出现任何空字符串。这在许多UI技术中很容易做到。 - Tuntable

10

需要记住的一件事是,NULL可能会使您的代码路径变得更加困难。例如,在Python中,大多数数据库适配器/ORM将 NULL 映射为 None

因此,像这样的事情:

print "Hello, %(title)s %(firstname) %(lastname)!" % databaserow

可能会导致输出 "Hello, None Joe Doe!" 为了避免这种情况,您需要类似于以下代码:

if databaserow.title:
    print "Hello, %(title)s %(firstname) %(lastname)!" % databaserow
else:
    print "Hello, %(firstname) %(lastname)!" % databaserow

这可能会使事情变得更加复杂。


25
在我看来,滥用数据库来“修复”代码或框架中的错误是一种(非常)糟糕的编程惯例。当没有数据可用时,你应该只插入NULL并保持一致性。否则,你必须使用类似于:if(myString == null || myString = "")这样的语句。当对象在你的代码中未设置或定义时,你也应该使用NULL而不是某种“占位符”(在我看来,空字符串就是一种占位符)。 - Gertjan
5
非常依赖于你选择的编程语言。在Python中,“if not myString:”测试为None和“”。这可能主要是文化问题。对于Java程序员来说,“不好的做法”在动态语言中被视为优雅。 - max

6
我不知道在这里应该采取什么最佳实践,但通常情况下,除非您希望null意味着与空字符串不同的含义,并且用户的输入与您的空字符串定义匹配,否则我会更倾向于使用null。
请注意,我是说您需要定义您希望它们不同的方式。有时候让它们不同是有意义的,有时候则不是。如果不是,请选择一个并坚持使用。像我说的那样,大多数时候我倾向于使用NULL。
哦,还要记住,如果列为null,则记录在几乎任何基于该列选择(具有where子句,在SQL术语中)的查询中,除非选择是针对null列的。

1
现在我看到上面的答案,我认为可以说你通常关心的区别是没有数据和空数据。 :-) - Platinum Azure

2
如果您在唯一索引中使用多列,并且其中至少一个列是必填的(即必填表单字段),如果您将索引中的其他列设置为NULL,则可能会出现重复行。这是因为唯一列中忽略NULL值。在这种情况下,使用唯一索引的其他列中的空字符串以避免重复行。
唯一索引中的列: (事件类型ID,事件标题,日期,位置,URL)
示例1: (1,“BBQ”,“2018-07-27”,null,null) (1,“BBQ”,“2018-07-27”,null,null)//允许并重复。
示例2: (1,“BBQ”,“2018-07-27”,'','') (1,“BBQ”,“2018-07-27”,'','')//不允许因为它是重复的。
以下是一些代码:
CREATE TABLE `test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `event_id` int(11) DEFAULT NULL,
  `event_title` varchar(50) DEFAULT NULL,
  `date` date DEFAULT NULL,
  `location` varchar(50) DEFAULT NULL,
  `url` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `event_id` (`event_id`,`event_title`,`date`,`location`,`url`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

现在将此插入以查看它是否允许重复行:
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`, 
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-27', NULL, NULL);

INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`, 
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-27', NULL, NULL);

现在插入这个并检查它是否被禁止:
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`, 
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-28', '', '');

INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`, 
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-28', '', '');

所以,这里没有对与错。由你决定哪种方式适合你的业务规则。


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