数据库字段允许空值,你会遇到一些问题。这里包含的一些知识点,跟大家聊一聊。数据准备:createtableuser(idint,namevarchar(20),index(id))engine=innodb;insertintouservalues(1,'shenjian');insertintouservalues(2,'zhangsan');insertintouservalues(3,'lisi');说明:id是一个索引,非唯一(nonunique),允许为空(null)。知识点1(热身):负查询不能命中索引,会导致全表扫描。explainselect*fromuserwhereid!=1;索引字段id不等于查询,如上图:type=ALL,全表扫描;rows=3,整个表只有3行;知识点2(重点):允许空值,不等于(!=)查询,可能会导致意想不到的结果。insertintouser(name)values('wangwu');先构造一条id为NULL的数据,可以看到一共有4条记录。选择*fromuserwhereid!=1;再次执行不相等查询。猜猜结果集中有多少条记录(总共4条,不等于排除1条)?错误的答案!结果集中只有2条记录,空值记录没有出现在结果集中。select*fromuserwhereid!=1oridisnull;如果你想得到预期的结果集,你必须添加一个或条件。画外音:恶心不恶心,这个大坑你踩过吗?知识点3(补充):某些or条件可能导致全表扫描。这时候应该作为一个union来优化。explainselect*fromuserwhereid=1;等价查询索引字段id可以命中索引,如上图所示:type=ref,使用非唯一索引;rows=1,估计扫描1行;解释选择*从用户whereidisnull;indexfieldid上面的空查询也可以命中索引,如上图:type=ref,非唯一索引;rows=1,估计扫描1行;explainselect*fromuserwhereid=1oridisnull;如果放在SQL语句中,使用orquery,就会扫描全表,如上图所示:type=ALL,扫描全表;rows=4,整个表只有4行;explainselect*fromuserwhereid=1unionselect*fromuserwhereidisnull;这时候应该优化成联合查询,可以命中索引了,如上图所示:type=ref,使用非唯一索引;rows=1,估计扫描1行;画外音:第三行临时表的ALL是两个结果集的合并。总结负比较(例如:!=)会触发全表扫描;如果允许空值,不等于(!=)的查询将不包括空值行(row),此时的结果集往往不是预期的,往往需要加一个or条件此时要包含空值(isnull)的结果;或者可能会造成全表扫描,此时可以优化为联合查询;创建表值时添加默认(default),避免空值的坑;解释工具是个好东西;希望大家有所收获!画外音:本文在MySQL5.6上测试。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文
