Summary 总结
null value always leads to many uncertainties when disposing sql statement.It may cause bad performance accidentally.
列中使用NULL
值容易引发不受控制的事情发生,有时候还会严重托慢系统的性能.
- null value will not be estimated in aggregate function() which may cause inaccurate results.
对含有NULL值的列进行统计计算,eg. count()
,max()
,min()
,结果并不符合我们的期望值. - null value will influence the behavior of the operations such as “distinct”,“group by”,“order by” which causes wrong sort.
干扰排序,分组,去重结果. - null value needs ifnull() function to do judgement which makes the program code more complex.
有的时候为了消除NULL
带来的技术债务,我们需要在SQL中使用IFNULL()
来确保结果可控,但是这使程序变得复杂. - null value needs a extra 1 byte to store the null information in the rows.
NULL
值并是占用原有的字段空间存储,而是额外申请一个字节去标注,这个字段添加了NULL
约束.(就像额外的标志位一样)
As these above drawbacks,it’s not recommended to define columns with default null.
We recommand to define “not null” on all columns and use zero number & vacant string to substitute relevant data type of null.
根据以上缺点,我们并不推荐在列中设置NULL作为列的默认值,你可以使用NOT NULL
消除默认设置,使用0
或者''
空字符串来代替NULL
.
MySQL中建表时可空(NULL)和非空(NOT NULL)的用法详解
更新时间:2020年07月27日 17:18:16 作者:MSSQL123
这篇文章主要介绍了MySQL中建表时可空(NULL)和非空(NOT NULL)的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
对于MySQL的一些个规范,某些公司建表规范中有一项要求是所有字段非空,意味着没有值的时候存储一个默认值。其实所有字段非空这么说应该是绝对了,应该说是尽可能非空,某些情况下不可能给出一个默认值。
那么这条要求,是基于哪些考虑因素,存储空间?相关增删查改操作的性能?亦或是其他考虑?该理论到底有没有道理或者可行性,本文就个人的理解,做一个粗浅的分析。
1,基于存储的考虑
这里对存储的分析要清楚MySQL数据行的存储格式,这里直接从这篇文章白嫖一部分结论,文章里分析的非常清楚(其实也是参考《MySQL技术内容Innodb存储引擎》)。
对于默认的Dynamic或者Compact格式的数据行结构,其行结构格式如下:
|变长字段长度列表(1~2字节)|NULL标志位(1字节)|记录头信息(5字节)|RowID(6字节)|事务ID(6字节)|回滚指针(7字节)|row content
1,对于变长字段,当相关的字段值为NULL时,相关字段不会占用存储空间。NULL值没有存储,不占空间,但是需要一个标志位(一行一个)。
2,对于变长字段,相关字段要求NOT NULL,存储成''的时候,也不占用空间,如果一个表中所有的字典都NOT NULL,行头不需要NULL的标志位
3,所有字段都是定长,不管是否要求为NOT NULL,都不需要标志位,同时不需要存储变长列长度
鉴于null值和非空(not null default '')两种情况,如果一个字段存储的内容是空,也就是什么都没有,前者存储为null,后者存储为空字符串'',两者字段内容本身存储空间大小是一样的。
但是如果一个表中存储在可空字段的情况下,其对应的数据行的头部,都需要一个1字节的NULL标志位,这个就决定了存储同样的数据,如果允许为null,相比not null的情况下,每行多了一个字节的存储空间的。
这个因素或者就是某些公司或者个人坚持“所有表禁止null字段”这个信仰的原因之一(个人持否定态度,可以尝试将数据库中所有的字段都至为not null 然后default一个值后会不会鸡飞狗跳)。
这里不再去做“微观”的分析,直接从“宏观”的角度来看一下差异。
测试demo
直接创建结构一致,但是一个表字段not null,一个表字段为null,然后使用存储此过程,两张表同时按照null值与非null值1:10的比例写入数据,也就是说每10行数据中1行数据字段为null的方式写入600W行数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | CREATE TABLE a ( id INT AUTO_INCREMENT, c2 VARCHAR (50) NOT NULL DEFAULT '' , c3 VARCHAR (50) NOT NULL DEFAULT '' , PRIMARY KEY (id) ); CREATE TABLE b ( id INT AUTO_INCREMENT, c2 VARCHAR (50), c3 VARCHAR (50), PRIMARY KEY (id) ); CREATE DEFINER=`root`@`%` PROCEDURE `create_test_data`( IN `loop_cnt` INT ) LANGUAGE SQL NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER COMMENT '' BEGIN DECLARE v2 , v3 VARCHAR (36); START TRANSACTION ; while loop_cnt>0 do SET v2 = UUID(); SET v3 = UUID(); if (loop_cnt MOD 10) = 0 then INSERT INTO a (c2,c3) VALUES ( DEFAULT , DEFAULT ); INSERT INTO b (c2,c3) VALUES ( DEFAULT , DEFAULT ); else INSERT INTO a (c2,c3) VALUES (v2,v3); INSERT INTO b (c2,c3) VALUES (v2,v3); END if ; SET loop_cnt=loop_cnt-1; END while; COMMIT ; |
a,b两张表生产完全一致的数据。
查看占用的存储空间情况,从information_schema.TABLES中查询这两个表的存储信息
1,一个字节的差别,体现在avg_row_length,a表因为所有的字段都是not null,因此相比b表,每行节省了每行节省了一个字节的存储
2,总得空间的差别:a表662683648/1024/1024=631.98437500MB,b表666877952/1024/1024=635.98437500MB,
也当前情况下,600W行数据有4MB的差异,差异在1%之内,其实实际情况下,字段多,table size更大的的时候,这个差异会远远小于1%。
就存储空间来说,你跟我说1T的数据库你在乎1GB的存储空间,随便一点数据/索引碎片空间,一点预留空间,垃圾文件空间,无用索引空间……,都远远大于可为空带来的额外这一点差异。
2,增删查改的效率
读写操作对比,通过连续读写一个范围之内的数据,来对比a,b两张表在读上面的情况。
2.1.)首先buffer pool是远大于table size的,因此不用担心物理IO引起的差异,目前两张表的数据完全都存在与buffer pool中。
2.1.)读测试操作放在MySQL实例机器上,因此网络不稳定引起的差异可以忽略。
增删查改的差异与存储空间的差异类似,甚至更小,因为单行相差1个字节,放大到600W+才能看到一个5MB级别的差异,增删查改的话,各种测试下来,没有发现有明显的差异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #!/usr/bin/env python3 import pymysql import time mysql_conn_conf = { 'host' : '127.0.0.1' , 'port' : 3306 , 'user' : 'root' , 'password' : '******' , 'db' : 'db01' } def mysql_read(table_name): conn = pymysql.connect(host = mysql_conn_conf[ 'host' ], port = mysql_conn_conf[ 'port' ], database = mysql_conn_conf[ 'db' ],user = mysql_conn_conf[ 'user' ],password = mysql_conn_conf[ 'password' ]) cursor = conn.cursor() try : cursor.execute( ''' select id,c2,c3 from {0} where id>3888888 and id<3889999;''' . format (table_name)) row = cursor.fetchall() except pymysql.Error as e: print ( "mysql execute error:" , e) cursor.close() conn.close() def mysql_write(loop,table_name): conn = pymysql.connect(host = mysql_conn_conf[ 'host' ], port = mysql_conn_conf[ 'port' ], database = mysql_conn_conf[ 'db' ],user = mysql_conn_conf[ 'user' ],password = mysql_conn_conf[ 'password' ]) cursor = conn.cursor() try : if loop % 10 = = 0 : cursor.execute( ''' insert into {0}} (c2,c3) values(DEFAULT,DEFAULT)''' . format (table_name)) else : cursor.execute( ''' insert into {1}} (c2,c3) values(uuid(),uuid())''' . format (table_name)) except pymysql.Error as e: print ( "mysql execute error:" , e) cursor.close() conn.commit() conn.close() if __name__ = = '__main__' : time_start = time.time() loop = 10 while loop> 0 : mysql_write(loop) loop = loop - 1 time_end = time.time() time_c = time_end - time_start print ( 'time cost' , time_c, 's' ) |
3,相关字段上的语义解析和逻辑考虑
这一点就观点差异就太多了,也是最容易引起口水或者争议的了。
1,对于字符类型,NULL就是不存在,‘'就是空,不存在和空本身就不是一回事,不太认同一定要NOT NULL,然后给出默认值。
2,对于字符类型,任何数据库中,NULL都是不等于NULL的,因为在处理相关字段上进行join或者where筛选的时候,是不需要考虑连接双方都为NULL的情况的,一旦用''替代了NULL,''是等于''的,此时就会出现与存储NULL完全不用的语义
3,对于字符类型,一旦将相关字段default成'',如何区分''与空字符串,比如备注字段,不允许为NULL,default成‘',那么怎么区分,NULL表达的空和默认值的空字符串''
4,对于相关的查询操作,如果允许为NULL,筛选非NULL值就是where *** is not null,语义上很清晰直观,一旦用字段非空,默认成'',会使用where *** <>''这种看起来超级恶心的写法,究竟要表达什么,语义上就已经开始模糊了
5,对于时间类型,绝大多数时候是不允许有默认值的,默认多少合适,当前时间合适么,千禧年2000合适么,2008年北京奥运会开幕时间合适么?
6,对于数值类型,比如int,比如decimal,在可空的情况下,如果禁止为NULL,默认给多少合适,0合适吗?-1合适吗?-9999999……合适吗?10086合适吗?1024合适吗?说实话,默认多少都不合适,NULL自身就是最合适的。
个人观点很明确,除非有特殊的需求要求一个字段绝对不能出现NULL值的情况,正常情况下,该NULL就NULL。
如果NULL没有存在的意义,干脆数据库就不要存在这个NULL就好了,事实上,哪个数据库没有NULL类型?
当然也不排除,某些DBA为了显得自己专业,弄出来一些莫须有的东西,现在就是有一种风气,在数据库上能提出来的限制条件越多,越有优越感。
想起来一个有关于默认值有意思的事,B站看视频的时候某up主曾提到过,因为B站把注册用户默认为男,出生日期某认为某个指定的日期,导致该up主在对用户点为分析后得到一些无法理解的数据。
个人认识有限,数据实话,非常想知道“所有字段非空”会带来什么其他哪些正面的影响,以及如何衡量这个正面的因素,还有,你们真的做到了,可以禁止整个实例下所有的库表中的字段禁止可空(nullable)?
MySQL中建表时可空(NULL)和非空(NOT NULL)的用法详解MySQL不推荐列默认值设置为null到底是为什么参考资料:https://blog.csdn.net/qq_30549099/article/details/107395521https://www.jb51.net/article/191848.htm必须把字段定义为NOT NULL并且提供默认值解读:null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化 null 这种...
select * from table where column is not null
带着上面几个疑问,我们来简单的研究一下null 和 not null 到底有什么不一样,他们之间的区别是什么以及各自的效率问题。
首先,我们要搞清楚“空值” 和 “NULL” 的概念:
1、空值是不占用空间的
2、mysql中的NU
今天用到了MySql里的isnull才发现他和MSSQL里的还是有点区别,现在简单总结一下:
mysql中isnull,ifnull,nullif的用法如下:
isnull(expr) 的用法:
如expr 为null,那么isnull() 的返回值为 1,否则返回值为 0。
mysql> select isnull(1+1);
mysql> select isnull(1/0);
使用= 的null 值对比通常是错误的。
isnull() 函数同 is null比较操作符具有一些相同的特性。请参见有关is null 的说明。
IFNULL(expr1,expr2)
当在创建表的时候,有声明哪个字段是NOT NULL,那么在插入值的时候,该字段的所在位置的值就不能为空,反之可以写null。如果创建表的时候声明了某个字段为NOT NULL ,而在插入值的时候在该字段的位置写null,系统就会报错。
具体命令行案例如下图:
在创建表的时候尽量把字段的默认值设置成 not null,除非你想存储null;因为在mysql中为null的的字段不会走索引,做统计的时候也不会被统计进去,如果想统计进去必须做特定的处理,这样做比较复杂。可以给字段的值设置成0、一个特殊的值或者一个空串代替空值。
MySQL难以优化引用了可空列的查询,它会使索引、索引统计和值更加复杂。
可空列需要更多的储存空间,还需要在MySQL内部进行特殊处...
相信很多用了MySQL很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问:
我字段类型是not null,为什么我可以插入空值
为毛not null的效率比null高
判断字段不为空的时候,到底要 select * from table where column <> ” 还是要用 select * from table wherecolumn is not null 呢。
带着上面几个疑问,我们来深入研究一下null 和 not null 到底有什么不一样。
首先,我们要搞清楚“空值” 和 “NULL” 的概念:
空值是不占用空间的
mysql中的NULL其实
mysql不能像oracle一样,使用nulls first 和nulls last,可以使用下面的语句代替
nulls first
order by IF(ISNULL(my_field),0,1), my_field;
nulls last
order by IF(ISNULL(my_field),1,0),my_field;
1. 占用空间区别:空值不占用任何存储空间,而空字符串占用一个字节的存储空间。
2. 插入/查询方式区别:插入空值时,可以直接将字段设置为null,而插入空字符串时,需要将字段设置为''。查询时,可以使用is null/is not null查询空值,而空字符串可以使用=或者!=、<、>等算术运算符进行查询。
3. COUNT和IFNULL函数:在使用COUNT函数统计某个字段的记录数量时,空值不会被计入,而空字符串会被计算在内。另外,IFNULL函数可以用于将空值替换为指定的值,但对空字符串无效。
综上所述,空值和空字符串在存储空间、插入/查询方式以及COUNT和IFNULL函数的处理上存在区别。<span class="em">1</span><span class="em">2</span><span class="em">3</span>
#### 引用[.reference_title]
- *1* *2* *3* [MySQL数据库中空值(null)和空字符串(‘‘)的区别,你不会不知道吧!](https://blog.csdn.net/weixin_46460843/article/details/118652015)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"]
[ .reference_list ]