带你读 MySQL 源码:Where 条件怎么过滤记录?( 二 )


  • 第 1 个 Item::val_bool() 代表 i2 > 20,经过多级调用 Arg_comparator::compare_int_signed() 判断记录的 i2 字段值是否大于 20 。
  • 第 2 个 Item::val_bool() 代表 i1 = 50 or i1 = 80 。
  • 第 2 个 Item::val_bool() 是复合条件,它的下层还嵌套了第 3、4 个 Item::val_bool():
  • 第 3 个 Item::val_bool() 代表 i1 = 50,经过多级调用 Arg_comparator::compare_int_signed() 判断记录的 i1 字段值是否等于 50 。
  • 第 4 个 Item::val_bool() 代表 i1 = 80,经过多级调用 Arg_comparator::compare_int_signed() 方法判断记录的 i1 字段值是否等于 80 。
第 3、4 个 Item::val_bool() 中只要有一个返回 true,第 2 个 Item::val_bool() 就会返回 true,表示记录匹配 i1 = 50 or i1 = 80 。
第 1、2 个 Item::val_bool() 必须都返回 true,Item_cond_and::val_int() 才会返回 1,表示记录匹配示例 SQL 的 where 条件 。
源码分析ExecuteIteratorQuery()// sql/sql_union.ccbool Query_expression::ExecuteIteratorQuery(THD *thd) {...{...for (;;) {// 从存储引擎读取一条记录int error = m_root_iterator->Read();DBUG_EXECUTE_IF("bug13822652_1", thd->killed = THD::KILL_QUERY;);// 读取出错,直接返回if (error > 0 || thd->is_error())// Fatal errorreturn true;// error < 0// 表示已经读完了所有符合条件的记录// 查询结束else if (error < 0)break;// SQL 被客户端干掉了else if (thd->killed)// Aborted by user{thd->send_kill_message();return true;}...// 发送数据给客户端if (query_result->send_data(thd, *fields)) {return true;}...}}...}这个方法是 select 语句的入口,属于重量级方法,在源码分析的第 1 篇文章《带你读 MySQL 源码:limit, offset》中也介绍过,但是,本文示例 SQL 的执行计划和之前不一样,这里有必要再介绍下 。
m_root_iterator->Read() 从存储引擎读取一条记录,对于示例 SQL 来说,m_root_iterator 是 FilterIterator 迭代器对象,实际执行的方法是 FilterIterator::Read() 。
FilterIterator::Read()int FilterIterator::Read() {for (;;) {int err = m_source->Read();if (err != 0) return err;bool matched = m_condition->val_int();if (thd()->killed) {thd()->send_kill_message();return 1;}/* check for errors evaluating the condition */if (thd()->is_error()) return 1;if (!matched) {m_source->UnlockRow();continue;}// Successful row.return 0;}}上面是 FilterIterator::Read() 方法的全部代码,代码量比较少,主要逻辑如下:
m_source->Read() 方法从存储引擎读取一条记录,因为示例 SQL 中 t1 表的访问方式为全表扫描,所以 m_source 是 TableScanIterator 迭代器对象 。
通过 explain 可以确认示例 SQL 中 t1 表的访问方式为全表扫描(type = ALL):
explain select * from t1where i2 > 20 and (i1 = 50 or i1 = 80)G***************************[ 1. row ]***************************id| 1select_type| SIMPLEtable| t1partitions| <null>type| ALLpossible_keys | <null>key| <null>key_len| <null>ref| <null>rows| 8filtered| 12.5Extra| Using wherem_source->Read() 从存储引擎读取一条记录之后,m_condition->val_int() 会判断这条记录是否匹配 where 条件 。
m_condition 代表 SQL 的 where 条件,对于示例 SQL 来说,它是 Item_cond_and 对象 。
m_condition->val_int() 实际执行的方法是 Item_cond_and::val_int(),这就是判断记录是否匹配示例 SQL where 条件的入口 。
compare_int_signed()// sql/item_cmpfunc.ccint Arg_comparator::compare_int_signed() {// 获取 where 条件操作符左边的值// 例如:i2 > 20// 获取当前读取记录的 i2 字段值longlong val1 = (*left)->val_int();if (current_thd->is_error()) return 0;// where 条件操作符左边的值不为 NULL// 才进入 if 分支if (!(*left)->null_value) {// 获取 where 条件操作符右边的值// 例如:i2 > 20// val2 的值就等于 20longlong val2 = (*right)->val_int();if (current_thd->is_error()) return 0;// where 条件操作符右边的值不为 NULL// 才进入 if 分支if (!(*right)->null_value) {// 到这里,where 条件操作符左右两边的值都不为 NULL// 把 where 条件的 null_value 设置为 falseif (set_null) owner->null_value = https://www.isolves.com/it/sjk/MYSQL/2023-05-26/false;// 接下来 3 行代码// 比较 where 条件操作符左右两边的值的大小if (val1 < val2) return -1;if (val1 == val2) return 0;return 1;}}// 如果执行到下面这行代码// 说明 where 条件操作符左右两边的值// 至少有一个是 NULL// 把 where 条件的 null_value 设置为 trueif (set_null) owner->null_value = true;return -1;}我们以 id = 2、3 的两条记录和示例 SQL 的 where 条件 i2 > 20 为例介绍 compare_int_signed() 的逻辑:


推荐阅读