MySQL中NULL字段的比较问题

最近有人问我MySQL中一个字段不论=NULL还是<>NULL都匹配不了数据,是在框架层实现的还是在存储引擎层实现的,我说你看看如果InnoDB表和MyISAM表都有这个现象,那就比较可能是在框架层。

当然这个答案跟没有回答一样,我们可以从代码上看看具体的实现部分。

 

1、              现象描述

表结构

CREATE TABLE `t` (

  `c` char(32) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=gbk

插入两行数据

insert into t (c) values('a'),(null);

查询

mysql> select * from t where c=null;

Empty set (0.00 sec)

 

mysql> select * from t where c<>null;

Empty set (0.00 sec)

 

mysql> select * from t where c is not null;

+------+

| c    |

+------+

| a    |

+------+

1 row in set (0.00 sec)

说明:从上面的三个查询可以看出,使用=null<>null都无法返回数据,只能通过isis not 来比较。

 

2、代码相关

      我们知道大概的流程,是引擎返回索引过滤后的结果,在框架层再依次判断记录是否符合条件。判断条件是否满足是在函数evaluate_join_record (sql_select.cc)中。

if (select_cond) 
{
  select_cond_result= test(select_cond->val_int()); /* check for errors evaluating the condition */

  if (join->thd->is_error())
    return NESTED_LOOP_ERROR;
}

if (!select_cond || select_cond_result)
 { ... } 

 

 

 

 

第三行的select_cond->val_int(),就是判断where的条件是否相同。其中select_cond的类型Item是一个虚类。我们从val_int()的函数注释中看到这样一句话“In case of NULL value return 0 and set null_value flag to TRUE.”,确认了这个是在框架层作的,而且是有意为之。

 

一路追查到这个函数int Arg_comparator::compare_string (sql/item_cmpfunc.cc),这个函数是用语判断两个字符串是否相同。

int Arg_comparator::compare_string()

{

  String *res1,*res2;

  if ((res1= (*a)->val_str(&value1)))

  {

    if ((res2= (*b)->val_str(&value2)))

    {   

      if (set_null)

        owner->nullvalue= 0;

      return sortcmp(res1,res2,cmp_collation.collation);

    }   

  }

  if (set_null)

    owner->nullvalue= 1;

  return -1;

}

 

函数返回值为0时表示相同,否则不同。

其中a是左值,b是右值。即如果输入的是 where ‘i’=c, a的值是’i’。从第4行和第6行的两个if看到,当ab中有一个是null的时候,不进入sortcmp,而是直接return -1

 

3、验证修改

       声明:这个只是为了验证结论修改,纯属练手,实际上现有的策略并没有问题。

 

int Arg_comparator::compare_string()   
{   
  String *res1,*res2;   
  res1= (*a)->val_str(&value1);   
  res2= (*b)->val_str(&value1);   
  if (!res1 && !res2)   
  {   
    return 0;   
  }   
  else if ((!res1 && res2) || (res1 && !res2))    
  {   
    return 1;   
  }   
  else  
  {   
    return sortcmp(res1,res2,cmp_collation.collation);   
  }   
}  

  

 

 

重新编译后执行效果如下

mysql> select * from t where c=null;

+--------------+

| c           |

+--------------+

| NULL    |

+--------------+

1 row in set (0.00 sec)

 

mysql> select * from t where c<>null;

+--------------+

| c        |

+--------------+

| a        |

+--------------+

1 row in set (0.00 sec)

 

 记得改回去。。。 ^_^

 

4、相关说明

a) Arg_comparator::compare_string() 这个函数只用于定义两个char[]的判断规则,因此修改后的执行程序中,非字符串字段判断仍为原来的规则,即=null<>null都认为不匹配。

b) 标准判断是否为null的用法是 where c is nullis not null。此时使用的判断函数为Item_func_isnull::val_int()Item_func_isnotnull::val_int() 这两个函数比较简单,直接用args[0]->is_null()判断

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值