mysql NULL的影响

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wll_1017/article/details/85062308


空值” 和 “NULL” 的概念

1、空值''是不占用空间的,mysql中的NULL其实是占用空间的
2、NULL 其实并不是空值,而是要占用空间,所以mysql在进行比较的时候,,所以对效率有一部分影响,而且B树索引时不会存储NULL值的,
  所以如果索引的字段可以为NULL,索引的效率会下降很多。
3、空值(null)是不能参与任何计算,因为空值参与任何计算都为空
4、在统计过程中,这个count函数会自动忽略空值的数据。此时统计出来的就是有值的信息。 如果采用的是空字符''的数据,则这个函数会将其统计进去
5、对于可为空的具有默认值的属性列  AUTO_INCREMENT、timestamp,会自动添加默认值 

空值跟null的区别。mysql官方:
“NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”

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

尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化。因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。

1:在进行count()统计某列的记录数的时候,如果采用的NULL值,会别系统自动忽略掉,但是空值是会进行统计到其中的。
2: 判断NULL 用IS NULL 或者 is not null,SQL 语句函数中可以使用ifnull()函数来进行处理,判断空字符用=''或者 <>''来进行处理
3: 对于MySQL特殊的注意事项,对于timestamp数据类型,如果往这个数据类型插入的列插入NULL值,则出现的值是当前系统时间。插入空值,则会出现 '0000-00-00 00:00:00'
4:对于空值的判断到底是使用is null 还是 =''要根据实际业务来进行区分。
5:空值('')是不占用空间的
6: MySQL中的NULL其实是占用空间的。

mysql不用null的理由
(1)所有使用NULL值的情况,都可以通过一个有意义的值的表示,这样有利于代码的可读性和可维护性,并能从约束上增强业务数据的规范性。

(2)NULL值到非NULL的更新无法做到原地更新,更容易发生索引分裂,从而影响性能。(null -> not null性能提升很小,除非确定它带来了问题,否则不要当成优先的优化措施)

(3)NULL值在timestamp类型下容易出问题,特别是没有启用参数explicit_defaults_for_timestamp

(4)NOT IN、!= 等负向条件查询在有 NULL 值的情况下返回永远为空结果,查询容易出错


NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.

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

NULL值产生问题的例子:
create table table1 (
    `id` INT (11) NOT NULL,
    `name` varchar(20) NOT NULL
)


create table table2 (
    `id` INT (11) NOT NULL,
    `name`  varchar(20)
)

insert into table1 values (4,"zhaoyun"),(2,"zhangfei"),(3,"liubei")
insert into table2 values (1,"zhaoyun"),(2, null)

(root@33306) [test]> select *from table1;
+----+----------+
| id | name     |
+----+----------+
|  4 | zhaoyun  |
|  2 | zhangfei |
|  3 | liubei   |
+----+----------+
3 rows in set (0.00 sec)

(root@33306) [test]> select *from table2;
+----+---------+
| id | name    |
+----+---------+
|  1 | zhaoyun |
|  2 | NULL    |
+----+---------+
2 rows in set (0.00 sec)


(1) NOT IN子查询在有NULL值的情况下返回永远为空结果,查询容易出错
select name from table1 where name not in (select name from table2 where id!=1)

(2) 列值允许为空,索引不存储null值,结果集中不会包含这些记录。
select * from table2 where name != 'zhaoyun'
+------+-------------+
|   id |      name   |
|------+-------------|
|      |             |
+------+-------------+
select * from table2 where name != 'zhaoyun1'
+------+-------------+
|   id |      name   |
|------+-------------|
|   1  |  zhaoyun    |
+------+-------------+

(3) 使用concat拼接时,首先要对各个字段进行非null判断,否则只要任何一个字段为空都会造成拼接的结果为null
select concat("1", null) from dual;

(root@33306) [test]> select id, concat(123, name) from table2;
+----+-------------------+
| id | concat(123, name) |
+----+-------------------+
|  1 | 123zhaoyun        |
|  2 | NULL              |
+----+-------------------+
2 rows in set (0.00 sec)


(4) 当计算count时候null column不会计入统计
 select count(name) from table2;
+-------------+
| count(name) |
+-------------+
|           1 |
+-------------+


 索引长度对比
alter table table1 add index idx_name (name);
alter table table2 add index idx_name (name);
explain select * from table1 where name='zhaoyun';
explain select * from table2 where name='zhaoyun';
table1的key_len = 82

table2的key_len = 83

key_len 的计算规则和三个因素有关:数据类型、字符编码、是否为 NULL
key_len 82 = 20 * 4(utf8mb4 - 4字节, utf8 - 3字节) + 2(存储varchar变长字符长度为2字节,定长字段无需额外的字节)
key_len 83 = 20 * 4(utf8mb4 - 4字节, utf8 - 3字节) + 2(存储varchar变长字符长度为2字节,定长字段无需额外的字节) + 1(是否为null的标志)

所以说索引字段最好不要为NULL,因为NULL会使索引、索引统计和值更加复杂,并且需要额外一个字节的存储空间。

 
 
(root@33306) [test]> explain select * from table1 where name='zhaoyun';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | table1 | NULL       | ref  | idx_name      | idx_name | 62      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)


(root@33306) [test]> explain select * from table2 where name='zhaoyun';
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | table2 | NULL       | ref  | idx_name      | idx_name | 63      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

 
 


NULL 其实并不是空值,而是要占用空间,所以mysql在进行比较的时候,NULL 会参与字段比较,所以对效率有一部分影响,而且B树索引时不会存储NULL值的,所以如果索引的字段可以为NULL,索引的效率会下降很多。

 CREATE TABLE `tt2` (
  `id` int(11) not null AUTO_INCREMENT,
  `name` char(1) NOT NULL DEFAULT '',
  primary key(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

 insert into t2 values(1,'');

(root@33306) [test]> select count(name) from t2;
+-------------+
| count(name) |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)


(root@33306) [test]> show index from t2;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t2    |          1 | tdate    |            1 | tdate       | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
| t2    |          1 | name     |            1 | name        | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
| t2    |          1 | num      |            1 | num         | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

(root@33306) [test]> select *From t2;
+------+------+------+---------------------+
| id   | name | num  | tdate               |
+------+------+------+---------------------+
|    1 | d    | NULL | NULL                |
|    2 | a    | NULL | NULL                |
|    2 | a    |    3 | NULL                |
|    3 | b    |    1 | 2018-12-17 10:26:11 |
|    4 | c    |    9 | 2018-12-16 10:26:11 |
|    4 | c    |    9 | 2018-12-10 10:26:11 |
+------+------+------+---------------------+


存在空值的列,加上索引后,还是会走索引的,但是索引里面不包含空值

(root@33306) [test]>  explain select  *from t2 where num>5;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | t2    | NULL       | range | num           | num  | 5       | NULL |    2 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

针对非空列的查询的时候会走全表扫描
(root@33306) [test]> explain select  *from t2 where  num is not null;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t2    | NULL       | ALL  | num           | NULL | NULL    | NULL |    6 |    66.67 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)


(root@33306) [test]>  explain select  *from t2 where num is null;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | t2    | NULL       | ref  | num           | num  | 5       | const |    2 |   100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
 

猜你喜欢

转载自blog.csdn.net/wll_1017/article/details/85062308