为什么不建议给MySQL设置Null值?《死磕MySQL系列 十八》

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 为什么不建议给MySQL设置Null值?《死磕MySQL系列 十八》

大家好,我是咔咔 不期速成,日拱一卒


之前ElasticSearch系列文章中提到了如何处理空值,若为Null则会直接报错,因为在ElasticSearch中当字段值为null时、空数组、null值数组时,会将其视为该字段没有值,最终还是需要使用exists或者null_value来处理空值


大多数ElasticSearch的数据都来自于各类数据库,这里暂且只针对于MySQL,各个开源软件中都默认兼容各种Null值,空数组等等


若从根源上截断就可以省很多事,直到现在很多开发小伙伴还是坚韧不拔的给字段的默认值还是Null


本期就来聊一聊为什么不建议给字段的默认值设置为Null


本期环境为:MySQL8.0.26




一、案例数据

创建表user


CREATE TABLE `user` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
 `age` tinyint(4) unsigned NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci


添加数据,共计10条数据,有两条数据的name值为Null


INSERT INTO `user` (`name`, `age`) VALUES ('kaka', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('niuniu', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yangyang', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('dandan', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('liuliu', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yanyan', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('leilie', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yao', 26);
INSERT INTO `user` (`name`, `age`) VALUES (NULL, 26);
INSERT INTO `user` (`name`, `age`) VALUES (NULL, 26);

一、count数据丢失

在这期 MySQL统计总数就用count,别花里胡哨的《死磕MySQL系列 十》 文章中,已经对count的使用说的非常明白了。


那借着这个案例,来分析一下为什么数据会丢失,先看结果


select count(*) as num1 ,count(name) as num2 from user;


image.png


使用count字段名时出现了数据丢失,很明显是因为主键ID9、10这两条记录的name值为空造成的。


为什么会出现这种情况?


当count除了主键字段外,会有两种情况:


一种是字段为null,执行时,判断到有可能是null,但还要把值取出来再判断下,不是null的进行累加


另一种是字段为not null,执行时,逐行从记录里边读出这个字段,判断不是null,才进行累加


此时,咱们遇到的问题是name字段的值存在了null值,所以会走第一种情况,不进行统计null值


为什么建议大家都使用count(*)?


MySQL对于count做了专门的优化,跟字段不同的是并不是把所有带了*的值取出来,而是指定了count(*)肯定不是null,只需要按行累加即可


MySQL团队对count(*)做了什么优化?


MySQL系列文章至今已经更新了第十八期了,你有没有猜到原因呢?


现在你应该知道主键索引结构中叶子节点存储的是整行数据,而普通索引叶子节点存储的是主键ID


那对于普通索引来说肯定会比主键索引小,因为对于MySQL来说,不管遍历哪个索引结果都一样,所以优化器会主动去找到那颗最小的树进行遍历。


在逻辑正确的前提下,尽量减少访问数据量,是数据库系统设计通用法则之一。


最后给大家留一个问题,为什么Innodb存储引擎不跟Myisam存储一样存储一个count值呢?


如果不知道的话,可以看上文提到的count文章


二、为distinct打抱不平

在开发工作中使用Distinct进行去重的场景十分的少,大多数情况都是使用group by完成的


select distinct name from user;


可以看到此时的数据依然是正确的,对Null值做了去重的操作


image.png


为什么要说这个,因为咔咔在其它的平台上看到过有人这么使用count(distinct name,mobile),然后说是统计出来的数据不准确。


这种用法依然是count(字段)的用法,distinct本身是会对Null进行去重,去重后依然是需要判断name的值不为null时,才会进行累计。


所以,不要把锅甩给distinct


三、使用表达式数据丢失

在一些值为null时,使用表达式会造成数据的不一致,接下来一起看下


select * from user where name != 'kaka';


image.png


这跟我们的预期结果不大一致,预期是想返回id2~10的数据


当然,这个问题也不是无解,MySQL同样也提供了方法


要解决这个问题,只能再加一个条件就是把字段值为null的再单独处理一下


image.png


四、空指针问题

如果一个列存在null值,使用MySQL的聚合函数后返回结果是null,而并非是0,就会造成程序执行时的指针异常


CREATE TABLE user_order (
 id INT PRIMARY KEY auto_increment,
 num int
) ENGINE='innodb';


insert into user_order(num) values(3),(6),(6),(NULL);


创建用户订单数量表,并插入4条数据,接下来演示一下产生的问题


select sum(num) from goods where id>4;


image.png


可以看到当字段为null时,使用聚合函数返回值就是null,并非是0,那么这个问题要怎么处理呢?


同样MySQL也给大家提供了对应函数,就是ifnull


select ifnull(sum(num), 0) from goods where id>4;

image.png



五、这是在难为谁?

当一个字段的值存在null值,若要进行null值查询时,必须要使用isnull或者ifnull进行匹配查询,又或者使用is null,is not null。


而常用的表达式就不能再进行使用了,有工作经验的还好的,要是新人的话会很难受。


接下来看几个新人经常犯的错误


错误一


对存在null值的字段使用表达式进行过滤,正确用法应该是is null 或者 is not null


select * from user where name<>null;


image.png


错误二


依然是使用表达式,同样可以使用isnull


image.png


六、总结

说了这么多也都感觉到了字段设置为null的麻烦之处,不过幸好的是MySQL对使用is null、isnull()等依然可以使用上索引。


咔咔目前所在的公司存在大量字段默认值就是null,于是代码中就大量存储ifnull、is null、is not null等代码。


一般字段数值类型的默认值就给成0,字符串的给个空也行,千万不要给null了哈!


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
17天前
|
关系型数据库 MySQL Linux
Linux系统如何设置自启动服务在MySQL数据库启动后执行?
【10月更文挑战第25天】Linux系统如何设置自启动服务在MySQL数据库启动后执行?
63 3
|
17天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
59 2
|
1月前
|
关系型数据库 MySQL 数据库连接
MySQL 表整行数据唯一性设置
MySQL 表整行数据唯一性设置
49 2
|
1月前
|
关系型数据库 MySQL 数据库
使用Docker部署的MySQL数据库如何设置忽略表名大小写?
【10月更文挑战第1天】使用Docker部署的MySQL数据库如何设置忽略表名大小写?
132 1
|
1月前
|
druid 关系型数据库 MySQL
开发指南048-mysql设置
如果链接的是mysql设置,需要做如下配置
|
2月前
|
安全 关系型数据库 MySQL
Navicat工具设置MySQL权限的操作指南
通过上述步骤,您可以使用Navicat有效地为MySQL数据库设置和管理用户权限,确保数据库的安全性和高效管理。这个过程简化了数据库权限管理,使其既直观又易于操作。
334 4
|
2月前
|
SQL 关系型数据库 MySQL
MySQL数据库中给表添加字段并设置备注的脚本编写
通过上述步骤,你可以在MySQL数据库中给表成功添加新字段并为其设置备注。这样的操作对于保持数据库结构的清晰和最新非常重要,同时也帮助团队成员理解数据模型的变化和字段的具体含义。在实际操作中,记得调整脚本以适应具体的数据库和表名称,以及字段的详细规范。
57 8
|
1月前
|
SQL 关系型数据库 MySQL
MySQL设置表自增步长
MySQL设置表自增步长
76 0
|
1月前
|
存储 关系型数据库 MySQL
MySQL 字符字段长度设置详解:语法、注意事项和示例
MySQL 字符字段长度设置详解:语法、注意事项和示例
182 0
|
3月前
|
SQL 关系型数据库 MySQL
mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据
对于数据库开发的专业人士来说,理解NULL的特性并知道如何正确地在查询中处理它们是非常重要的。以上所介绍的技巧和实例可以帮助你更精准地执行数据库查询,并确保数据的完整性和准确性。在编写代码和设计数据库结构时,牢记这些细节将有助于你避免许多常见的错误,提高数据库应用的质量与性能。
119 0

热门文章

最新文章