2024-11-09
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/5419154785

建议 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,然后设置默认值即可。

阅读全文