要盡可能地把字段定義為 NOT NULL,即使應用程序無須保存 NULL(沒有值),也有許多表包含了可空列(Nullable Column)
這僅僅是因為它為默認選項。除非真的要保存 NULL,否則就把列定義為 NOT NULLMySQL難以優化引用了可空列的查詢,它會使索引、索引統計和值更加復雜。
可空列需要更多的儲存空間,還需要在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 | +------+--------+
