【数据库】表字段设计时不推荐使用可空值(NULL)

本文详细探讨了MySQL中避免使用NULL值的原因,包括对查询性能的影响、存储空间占用和代码可维护性。通过实例展示了NOTIN子查询、空值查询、字段拼接和统计行为的变化,并强调了使用有意义的值替代NULL的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【Mysql】数据库系列



前言

在表设计字段时,不推荐使用可空值,推荐使用一个有意义的值去代替NULL,这样有利于代码的可读性和可维护性,并能从约束上增强业务数据的规范性。《高性能mysql》中给出的理由是:

Mysql难以优化引用可空列查询,它会使索引、索引统计和值更加复杂。可空列需要更多的存储空间,还需要mysql内部进行特殊处理。可空列被索引后,每条记录都需要一个额外的字节,还能导致MYisam 中固定大小的索引变成可变大小的索引。
下面举例子说明。


一、表和数据准备

创建表:student-01(name可以为NULL)、student-02(name不可以为NULL)
DDL:

-- `student-01` definition

CREATE TABLE `student-01` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `age` int DEFAULT '0' COMMENT '年龄',
  `name` varchar(50) DEFAULT NULL COMMENT '名字',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表01';
-- `student-02` definition

CREATE TABLE `student-02` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `age` int DEFAULT '0' COMMENT '年龄',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '名字',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

数据:

INSERT INTO `student-01` (id, age, name) VALUES(1, 12, '小米1');
INSERT INTO `student-01` (id, age, name) VALUES(2, 23, '小rose1');
INSERT INTO `student-01` (id, age, name) VALUES(3, 12, '小jeck1');
INSERT INTO `student-01` (id, age, name) VALUES(4, 8, '小ming');
INSERT INTO `student-01` (id, age, name) VALUES(5, 11, '');
INSERT INTO `student-01` (id, age, name) VALUES(6, 46, 'NULL1');
INSERT INTO `student-01` (id, age, name) VALUES(7, 14, '小xu');
INSERT INTO `student-01` (id, age, name) VALUES(8, 11, NULL);
INSERT INTO `student-02` (id, age, name) VALUES(1, 12, '小米');
INSERT INTO `student-02` (id, age, name) VALUES(2, 23, '小rose');
INSERT INTO `student-02` (id, age, name) VALUES(3, 12, '小jeck');
INSERT INTO `student-02` (id, age, name) VALUES(4, 8, '');
INSERT INTO `student-02` (id, age, name) VALUES(5, 11, '');
INSERT INTO `student-02` (id, age, name) VALUES(6, 46, '小xiong');
INSERT INTO `student-02` (id, age, name) VALUES(7, 14, '小xu');

二、验证

1.NOT IN子查询在有NULL值的情况下返回永远为空结果

查询student-02中名字不存在student-01中的,推测的结果中应该有:小米、小rose、小jeck、小xiong,这几个只存在student-02。

select
	*
from
	`student-02` s
where
	s.name not in(
	select
		s2.name
	from
		`student-01` s2 )

运行结果:空,什么多没有。
在这里插入图片描述

2.使用!=去查询可空值字段时,数据中存在NULL,NULL记录查询不到

查询student-01中名字不等“小米1”的:

select * from `student-01` s where s.name  != '小米1'

输出结果:NULL值不包含在其中,id=8的记录没有包含进去。
在这里插入图片描述

3.如果在两个字段进行拼接:比如前缀+名字,字段为空会造成拼接的结果为NULL

将student-01表中name值加上“kexuexiong-”前缀:

select  s.id,CONCAT("kexuexiong-",s.name ) as name from `student-01` s

输出结果:
在这里插入图片描述

4.如果有 Null column 存在的情况下,count(Null column),null 值不会参与统计

统计名字有多少个:

select count(s.name ) from `student-01` s 

输出结果:
在这里插入图片描述

5. Null 字段的判断方式

使用!=NULL

select * from `student-01` s where s.name != null

输出结果;
在这里插入图片描述
使用=NULL

select * from `student-01` s where s.name = null

在这里插入图片描述
上面这两种方式是新手经常犯的错误,正确的方式应该是:
使用is not null

select * from `student-01` s where s.name is not null

使用is null

select * from `student-01` s where s.name is null

6.可空字段索引,会增加额外的空间

给student-01的name增加索引:

CREATE INDEX student_01_name_IDX USING BTREE ON `student-01` (name);

给student-02的name增加索引:

CREATE INDEX student_02_name_IDX USING BTREE ON `student-02` (name);

使用explain分析sql:

explain select * from `student-01` s where s.name = '小米1'
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEsrefstudent_01_name_IDXstudent_01_name_IDX203const1100.0
explain select * from `student-02` s where s.name = '小米'
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEsrefstudent_02_name_IDXstudent_02_name_IDX202const1100.0

两条查询语句都走name索引,但是key_len(索引字段的长度)不同。可空值得student-01(203)中比不可空值的student-02(202)多了1,两者类型都为varchar(50),编码集:utf8mb4。

key_len 的计算规则和三个因素有关:数据类型、字符编码、是否为 NULL

  • key_len 202 == 50*4(utf8 4字节) + 2 (存储 varchar 变长字符长度 2字节,定长字段无需额外的字节)

  • key_len 203== 20*4(utf8mb4 4字节) + 1 (是否为 Null 的标识) + 2 (存储 varchar 变长字符长度 2字节,定长字段无需额外的字节)


总结

综合以上的案列分析,不建议字段设计时使用可空值。推荐使用一个有意义的值去代替NULL,这样有利于代码的可读性和可维护性,并能从约束上增强业务数据的规范性。

在Informix数据库中创建表字段涉及SQL语句的使用。下面是一些基础信息帮助您了解如何操作。 首先,需要明确的是,在Informix里创建包含特定字段的新一般通过`CREATE TABLE`命令完成。语法结构大致如下: ```sql CREATE TABLE ( 字段1 数据类型 [完整性约束], 字段2 数据类型 [完整性约束], ... ); ``` 每个字段由名称、数据类型以及任选的一组完整性约束条件构成。例如常见的字符型(CHAR)、整数型(INTEGER)、日期间型(DATETIME)等都是可用的数据类型;而主键(`PRIMARY KEY`)、非空(`NOT NULL`)之类的则是属于完整性约束的例子。 这里给出一个具体的例子来说明怎样在一个新的客户信息(customers)中添加一些基本字段: ```sql CREATE TABLE customers( customer_id SERIAL PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50) NOT NULL, email_address VARCHAR(100), birth_date DATE ); ``` 上述代码片段做了几件事情: - 定义了一个名为customer_id的序列(SERIAL)类型的自动递增列作为主键。 - 分别指定了first_name和last_name两个用于存储名字字符串的VARCHAR变量长度字符串,并强制规定姓(last_name)能为空值(not null)。 - 加入了email_address这样一个可以为空也可以为空的最大长度超过一百个字符的位置存放邮箱地址。 - 最后加入了一项出生日期birth_date,它是一个典型的DATE类型的实例化应用案例。 当涉及到更复杂的业务需求如设置默认值(DEFAULT),唯一性限制(UNIQUE),或者是外键(Foreign Key)关联其他格的候,则可以在相应的字段定义部分增加额外选项。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科学熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值