mysql 索引列為Null的走不走索引及null在統計時的問題


要盡可能地把字段定義為 NOT NULL,即使應用程序無須保存 NULL(沒有值),也有許多表包含了可空列(Nullable Column)
這僅僅是因為它為默認選項。除非真的要保存 NULL,否則就把列定義為 NOT NULL

MySQL難以優化引用了可空列的查詢,它會使索引、索引統計和值更加復雜。
可空列需要更多的儲存空間,還需要在MySQL內部進行特殊處理。當可空列被索引的時候,
每條記錄都需要一個額外的字節,還可能導致 MyISAM 中固定大小的索引(例如一個整數列上的索引)變成可變大小的索引。
即使要在表中儲存「沒有值」的字段,還是有可能不使用 NULL 的,考慮使用 0、特殊值或空字符串來代替它。
把 NULL 列改為 NOT NULL 帶來的性能提升很小,所以除非確定它引入了問題,否則就不要把它當作優先的優化措施。
然后,如果計划對列進行索引,就要盡量避免把它設置為可空,雖然在mysql里 Null值的列也是走索引的

 mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
|         0 |             1 |
+-----------+---------------+
1 row in set

mysql> SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
+-----------+---------------+------------+----------------+
| 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL |
+-----------+---------------+------------+----------------+
|         0 |             1 |          0 |              1 |
+-----------+---------------+------------+----------------+

測試

CREATE TABLE `test_null` (
  `id` int(11) DEFAULT NULL,
  `mark` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


create procedure test_null(in num int)
BEGIN
DECLARE i int;  
set i=1;  
while (i<num) 
DO 
  if mod(i,10)!=0 then 
     insert into test_null values (i,concat('aaa',i));
   else
     insert into test_null values (null,concat('aaa',i));
   end if;
set i=i+1;  
END while;  
END;

call test_null(10000);

 

mysql> select count(*) from test_null;
+----------+
| count(*) |
+----------+
|     9999 |
+----------+
沒加任何索引時
mysql> explain SELECT * from test_null WHERE id is null;
+----+-------------+-----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | test_null | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10105 |       10 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+-------+----------+-------------+
在id上加普通索引
create index idx_test_null on test_null(id);
mysql> explain SELECT * from test_null WHERE id is null;
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+
| id | select_type | table     | partitions | type | possible_keys | key           | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | test_null | NULL       | ref  | idx_test_null | idx_test_null | 5       | const |  999 |      100 | Using index condition |
+----+-------------+-----------+------------+------+---------------+---------------+---------+-------+------+----------+-----------------------+
null值也是走索引的

 null在count統計時時的問題

create table test (id int,val int); 
INSERT INTO `test` VALUES ('1', '11');
INSERT INTO `test` VALUES ('1', '111');
INSERT INTO `test` VALUES ('2', '2');
INSERT INTO `test` VALUES ('2', '22');
INSERT INTO `test` VALUES ('2', '222');

1條語句統計id=1,id=2的個數

一般錯誤寫法

select count(id=1) ,count(id=2) from test;

mysql> select count(id=1) ,count(id=2) from test;
+-------------+-------------+
| count(id=1) | count(id=2) |
+-------------+-------------+
|           5 |           5 |
+-------------+-------------+

需要注意count只不會統計null的列,0的會統計

mysql> select 1 or null as or1,1 and null as and1 ,0 and null as and0 ,0 or null as null0;
+------+------+------+-------+
| or1  | and1 | and0 | null0 |
+------+------+------+-------+
|    1 | NULL |    0 |  NULL |
+------+------+------+-------+
mysql> select id=1 ,id=2 from test;
+------+------+
| id=1 | id=2 |
+------+------+
|    1 |    0 |
|    1 |    0 |
|    0 |    1 |
|    0 |    1 |
|    0 |    1 |
+------+------+

要把0值變為null,count時不計算即可

 

mysql> select count(id=1) ,count(id=2) from test;
+-------------+-------------+
| count(id=1) | count(id=2) |
+-------------+-------------+
|           5 |           5 |
+-------------+-------------+
mysql> select id=1 or null,id=2 or null from test;
+--------------+--------------+
| id=1 or null | id=2 or null |
+--------------+--------------+
|            1 |         NULL |
|            1 |         NULL |
|         NULL |            1 |
|         NULL |            1 |
|         NULL |            1 |
+--------------+--------------+

 

所以正確的寫法是

mysql> select count(id=1 or null),count(id=2 or null) from test;
+---------------------+---------------------+
| count(id=1 or null) | count(id=2 or null) |
+---------------------+---------------------+
|                   2 |                   3 |
+---------------------+---------------------+
1 row in set (0.00 sec)

select id,count(id) from test where id in(1,2) GROUP BY id  

常數與null的運算

DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` (
  `id` int(11) DEFAULT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of test1
-- ----------------------------
INSERT INTO `test1` VALUES ('1', '5', '1');
INSERT INTO `test1` VALUES ('2', '6', null);
INSERT INTO `test1` VALUES ('3', '4', '2');
INSERT INTO `test1` VALUES ('4', '7', null);
INSERT INTO `test1` VALUES ('5', null, null);

查詢 id,a-b的數量(剩余計算)

錯誤寫法

mysql> SELECT id ,(a-b) as remain from test1;
+------+--------+
| id   | remain |
+------+--------+
|    1 |      4 |
|    2 |   NULL |
|    3 |      2 |
|    4 |   NULL |
|    5 |   NULL |
+------+--------+

正確寫法

mysql> SELECT id ,(IFNULL(a,0)-IFNULL(b,0)) as remain  from test1;
+------+--------+
| id   | remain |
+------+--------+
|    1 |      4 |
|    2 |      6 |
|    3 |      2 |
|    4 |      7 |
|    5 |      0 |
+------+--------+

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM