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都无法返回数据,只能通过is或is 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看到,当a和b中有一个是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 null和is not null。此时使用的判断函数为Item_func_isnull::val_int()和Item_func_isnotnull::val_int() , 这两个函数比较简单,直接用args[0]->is_null()判断。

时间: 2024-08-17 18:08:35

MySQL中NULL字段的比较问题的相关文章

MySQL中timestamp字段的一些规则

有同学说timestamp字段的规则比较"诡异".手册里面说的比较复杂,这里简单说明一下MySQL中 timestamp字段的一些规则.   分为两部分   定义规则:   1.  timestamp字段有三个属性: a)           是否允许NULL.默认为not null. b)           默认值.可以设定为default CURRENT_TIMESTAMP 或default 某个常量.若定义时缺省,对于第一个出现的timestamp字段,默认为CURRENT_T

MySQL中各种字段的取值范围

mysql MySQL中各种字段的取值范围过节回来,网站更新的第一篇文章.看来我有必要在最近找到一位志同道合的同学一起来维护站点才行了----------------------------------------------------------------TINYINT -128 - 127 TINYINT UNSIGNED 0 - 255 SMALLINT -32768 - 32767 SMALLINT UNSIGNED 0 - 65535 MEDIUMINT -8388608 - 83

MySQL中NULL与空字符串 空格问题

  一些刚刚接触MySQL的孩子,经常会错误的认为NULL与空字符串' '是相同的.这看似是一件不重要的事情,但是在MySQL中,这两者是完全不同的.NULL是指没有值,而''则表示值是存在的,只不过是个空值.. 这就相当于厂子给职工分房子,一共只有一套,老张和老李都想要,可是经理告诉老张,下次有您的房子,可是老李压根就没人搭理他.所以老张的房子是"空"的,因为这是空白支票,不过毕竟经理张口了,而老李的房子就是NULL的,因为根本就没有人考虑过他. 二者的区别不大,但是如果不仔细的话可

mysql中null,not null,default,auto_increment详解

NULL 和 NOT NULL 修饰符: 可以在每个字段后面都加上这NULL 或 NOT NULL 修饰符来指定该字段是否可以为空(NULL),还是说必须填上数据(NOT NULL).MySQL默认情况下指定字段为NULL修饰符,如果一个字段指定为NOT NULL,MySQL则不允许向该字段插入空值(这里面说的空值都为NULL),因为这是"龟定".  代码如下 复制代码 /* 创建好友表,其中id ,name ,pass都不能为空 */ create table friends ( i

Mysql中NULL使用方法与注意事项

SELECT NULL =0, NULL =12345, NULL <>12345, NULL +12345, NULL || 'abc', NULL = NULL , NULL <> NULL , NULL AND TRUE , NULL AND FALSE , NULL OR FALSE , NULL OR TRUE , NOT (NULL); 如果这是一道面试题,估计不知道有多少程序员甚至是DBA会阵亡-- 正确的答案是什么?(为了加深印象,建议复制SQL到mysql里去执行

MYsql中NULL与空字符串空区别详解

对于SQL的新手,NULL值的概念常常会造成混淆,他们常认为NULL与MySQL空字符串是相同的事.情况并非如此.例如,下述语句是完全不同的:MySQL> INSERT INTO my_table (phone) VALUES (NULL);  代码如下 复制代码 mysql> INSERT INTO my_table (phone) VALUES ('');   这两条语句均会将值插入phone(电话)列,但第1条语句插入的是NULL值,第2条语句插入的是空字符串.第1种情况的含义可被解释为

MySQL中BLOB字段类型介绍

BLOB类型的字段用于存储二进制数据 MySQL中,BLOB是个类型系列,包括:TinyBlob.Blob.MediumBlob.LongBlob,这几个类型之间的唯一区别是在存储文件的最大大小上不同. MySQL的四种BLOB类型 类型 大小(单位:字节) TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G

ORACLE MYSQL中join 字段类型不同索引失效的情况

关于JOIN使用不同类型的字段类型,数据库可能进行隐士转换,MYSQL ORACLE都是如此, 下面使用一个列子来看看,脚本如下: mysql: drop table testjoin1; drop table testjoin2; create table testjoin1(id int, name varchar(20)); create table testjoin2(id varchar(20),name varchar(20),key(id); oracle: drop table

mysql中从字段中URL提取域名信息

如果你有一字段dm记录了一个url,为了更好的优化模糊查询速度或统计速度,在数据表原有的结构上增加3个字段,分别为  `sdm` varchar(64) NOT NULL,   #subdomain记录子域名,如:123456.user.qzone.qq.com   `tdm` varchar(32) NOT NULL,    #topdomain记录一级域名,如:qq.com   `rdm` varchar(8) NOT NULL,      #rootdomain记录根域,如:com 操作顺