建议 MySQL 数据库字段要设置 NOT NULL。这句建议你可能听过很多人说过,也见过一些规范里面有写,但是你有没有仔细想过为什么呢?对于是说过 NOT NULL 还是 NULL,其实都有一定的道理,没有强制要求,对错之分,都是规范而已。
建议设置为 NOT NULL 一般是基于如下几个理由。
为了下面案例的演示,我们需要使用如下表结构和数据:
CREATE TABLE user (
id bigint(20) PRIMARY KEY AUTO_INCREMENT comment 'id',
name varchar(20) comment '姓名',
gender char(1) comment '性别 1男 0 女'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户';
语义不清晰
需要注意 NULL 和空值所代表的含义是不一样的。NULL 表示的是未知,而空值所表示的是没有,它是有意义的,有“值”的,只不过是空的而已。
由于 NULL 表示“未知”,这种未知在有些场景会带来业务逻辑上面的混淆,使用 NOT NULL 后,可以确保每个字段都有明确的、有意义的值。
聚合函数计算不准确
对于 NULL 值的列,使用聚合函数
count()
的时候会忽略 null 值,导致计算不准确。
比如,我有如下数据:
执行以下 SQL
select count(*),count(name),count(gender) from user
通过 count(name)
求出来的值其实与我们的预期是不相符的。
count(1)
、count(*)
是对表中的行数进行统计。count(name)
是对表中非NULL的列进行统计。
= 和 ≠ 失效
对于NULL值的列,是不能使用 " = " 表达式进行判断的,比如:
使用 name ≠ null
同样查询不到数据,我们应使用 is null
或者 is not null
。
在实际开发过程中,由于字段设置为了允许 null,导致我们在查询非空的时候需要这样写:
select * from user where name != '' and name is not null;
如果我们查询 name ≠ 'zhangsan'
的数据时,你会发现那两条 NULL 的数据查不到:
NULL与其他值运算结果为NULL
NULL和其他任何值进行运算结果都是NULL,包括表达式的值也是NULL。比如我们对 name 使用 concat()
:
在工作中,你碰到这种情况你不懵逼啊?
对distinct、group by、order by等排序结果的影响
- 对distinct 、 group by来说,所有的 NULL 值都会被视为相等。
- 对于 order by 来说升序 NULL 会排在最前。
浪费存储空间
在 MySQL 中存储 NULL 值确实可能比存储非 NULL 值占用更多的存储空间,这主要是因为 MySQL 需要额外的信息来标记该值为 NULL。
在 MySQL 中,如果存储引擎为 InnoDB。当一个列可以为 NULL 时,MySQL 需要额外的空间来存储每个值的“NULL 状态”,需要在记录头中占用一个额外的位(bit)来标记其是否为 NULL。
最后
来自高性能Mysql中有这样一段话:
尽量避免NULL。 很多表都包含可为NULL(空值)的列,即使应用程序并不需要保存NULL也是如此,这是因为可为NULL是列的默认属性。通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。 如果查询中包含可为NULL的列,对MySql来说更难优化,因为可为NULL的列使得索引、索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间,在MySql里也需要特殊处理。当可为NULL的列被索引时,每个索引记录需要一个额外的字节,在MyISAM里甚至还可能导致固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。 通常把可为NULL的列改为NOT NULL带来的性能提升比较小,所以(调优时)没有必要首先在现有schema中查找并修改掉这种情况,除非确定这会导致问题。但是,如果计划在列上建索引,就应该尽量避免设计成可为NULL的列。 当然也有例外,例如值得一提的是,InnoDB使用单独的位(bit)存储NULL值,所以对于稀疏数据有很好的空间效率。但这一点不适用于MyISAM。
所以,一般来说都不建议 MySQL 的列设置为 NULL,而是选择 NOT NULL,然后设置默认值即可。