精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

帶你讀 MySQL 源碼:Where 條件怎么過(guò)濾記錄?

數(shù)據(jù)庫(kù) MySQL
從存儲(chǔ)引擎讀取一條記錄之后,對(duì) Or 連接的 N 個(gè) Where 條件(N >= 2)調(diào)用 Item->val_bool(),只要其中一個(gè)返回值等于True,記錄就匹配 Or 連接的 N 個(gè) Where 條件。

我們來(lái)聊聊 MySQL 是怎么判斷一條記錄是否匹配 where 條件的。

本文內(nèi)容基于 MySQL 8.0.32 源碼。

正文

準(zhǔn)備工作

創(chuàng)建測(cè)試表:

CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `str1` varchar(255) DEFAULT '',
  `i1` int DEFAULT '0',
  `i2` int DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入測(cè)試數(shù)據(jù):

INSERT INTO t1(str1, i1, i2) VALUES
('s1', NULL, NULL),
('s2', 20, NULL),
('s3', 30, 31),
('s4', 40, 41),
('s5', 50, 51),
('s6', 60, 61),
('s7', 70, 71),
('s8', 80, 81);

示例 SQL:

select * from t1
where i2 > 20 and (i1 = 50 or i1 = 80)

整體介紹

在源碼中,where 條件會(huì)形成樹(shù)狀結(jié)構(gòu),示例 SQL 的 where 條件結(jié)構(gòu)如下:

注意:這里的樹(shù)狀結(jié)構(gòu)不是數(shù)據(jù)結(jié)構(gòu)中的樹(shù)。

圖片

我們可以從圖中得到以下信息:

  • Item_cond_and 代表 where 條件中的 and,連接 Item_func_gt 和 Item_cond_or。
  • Item_func_gt 代表 i2 > 20,其中 Item_field 包含 Field_long,代表 i2 字段,Item_int 代表整數(shù) 20。
  • Item_cond_or 代表 where 條件中的 or,連接兩個(gè) Item_func_eq。
  • 第 1 個(gè) Item_func_eq 代表 i1 = 50,其中 Item_field 包含 Field_long,代表 i1 字段,Item_int 代表整數(shù) 50。
  • 第 2 個(gè) Item_func_eq 代表 i1 = 80,其中 Item_field 包含 Field_long,代表 i1 字段,Item_int 代表整數(shù) 80。

接下來(lái),我們結(jié)合堆棧來(lái)看看 where 條件的實(shí)現(xiàn)流程:

| > mysql_execute_command(THD*, bool) sql/sql_parse.cc:4688
| + > Sql_cmd_dml::execute(THD*) sql/sql_select.cc:578
| + - > Sql_cmd_dml::execute_inner(THD*) sql/sql_select.cc:778
| + - x > Query_expression::execute(THD*) sql/sql_union.cc:1823
| + - x = > Query_expression::ExecuteIteratorQuery(THD*) sql/sql_union.cc:1770
| + - x = | > FilterIterator::Read() sql/iterators/composite_iterators.cc:79
| + - x = | + > Item_cond_and::val_int() sql/item_cmpfunc.cc:5973
| + - x = | + - > // 第 1 個(gè) Item::val_bool()
| + - x = | + - > // 代表 i2 > 20
| + - x = | + - > Item::val_bool() sql/item.cc:218
| + - x = | + - x > Item_func_gt::val_int() sql/item_cmpfunc.cc:2686
| + - x = | + - x = > Arg_comparator::compare() sql/item_cmpfunc.h:210
| + - x = | + - x = | > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826
| + - x = | + - x = | + > Item_field::val_int() sql/item.cc:3013
| + - x = | + - x = | + - > Field_long::val_int() const sql/field.cc:3763 // i2
| + - x = | + - x = | + > Item_int::val_int() sql/item.h:4934 // 20
| + - x = | + - > // 第 2 個(gè) Item::val_bool()
| + - x = | + - > // 代表 i1 = 50 or i1 = 80
| + - x = | + - > Item::val_bool() sql/item.cc:218
| + - x = | + - x > Item_cond_or::val_int() sql/item_cmpfunc.cc:6017
| + - x = | + - x = > // 第 3 個(gè) Item::val_bool()
| + - x = | + - x = > // 代表 i1 = 50
| + - x = | + - x = > Item::val_bool() sql/item.cc:218
| + - x = | + - x = | > Item_func_eq::val_int() sql/item_cmpfunc.cc:2493
| + - x = | + - x = | + > Arg_comparator::compare() sql/item_cmpfunc.h:210
| + - x = | + - x = | + - > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826
| + - x = | + - x = | + - x > Item_field::val_int() sql/item.cc:3013
| + - x = | + - x = | + - x = > Field_long::val_int() const sql/field.cc:3763 // i1
| + - x = | + - x = | + - x > Item_int::val_int() sql/item.h:4934 // 50
| + - x = | + - x = > // 第 4 個(gè) Item::val_bool()
| + - x = | + - x = > // 代表 i1 = 80
| + - x = | + - x = > Item::val_bool() sql/item.cc:218
| + - x = | + - x = | > Item_func_eq::val_int() sql/item_cmpfunc.cc:2493
| + - x = | + - x = | + > Arg_comparator::compare() sql/item_cmpfunc.h:210
| + - x = | + - x = | + - > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826
| + - x = | + - x = | + - x > Item_field::val_int() sql/item.cc:3013
| + - x = | + - x = | + - x = > Field_long::val_int() const sql/field.cc:3763 // i1
| + - x = | + - x = | + - x > Item_int::val_int() sql/item.h:4934 // 80

FilterIterator::Read() 從存儲(chǔ)引擎讀取一條記錄,Item_cond_and::val_int() 判斷該記錄是否匹配 where 條件。

從堆棧中可以看到,Item_cond_and::val_int() 的下一層有兩個(gè) Item::val_bool():

  • 第 1 個(gè) Item::val_bool() 代表 i2 > 20,經(jīng)過(guò)多級(jí)調(diào)用 Arg_comparator::compare_int_signed() 判斷記錄的 i2 字段值是否大于 20。
  • 第 2 個(gè) Item::val_bool() 代表 i1 = 50 or i1 = 80。
  • 第 2 個(gè) Item::val_bool() 是復(fù)合條件,它的下層還嵌套了第 3、4 個(gè) Item::val_bool():
  • 第 3 個(gè) Item::val_bool() 代表 i1 = 50,經(jīng)過(guò)多級(jí)調(diào)用 Arg_comparator::compare_int_signed() 判斷記錄的 i1 字段值是否等于 50。
  • 第 4 個(gè) Item::val_bool() 代表 i1 = 80,經(jīng)過(guò)多級(jí)調(diào)用 Arg_comparator::compare_int_signed() 方法判斷記錄的 i1 字段值是否等于 80。

第 3、4 個(gè) Item::val_bool() 中只要有一個(gè)返回 true,第 2 個(gè) Item::val_bool() 就會(huì)返回 true,表示記錄匹配 i1 = 50 or i1 = 80。

第 1、2 個(gè) Item::val_bool() 必須都返回 true,Item_cond_and::val_int() 才會(huì)返回 1,表示記錄匹配示例 SQL 的 where 條件。

源碼分析

ExecuteIteratorQuery()

// sql/sql_union.cc
bool Query_expression::ExecuteIteratorQuery(THD *thd) {
  ...
  {
    ...
    for (;;) {
      // 從存儲(chǔ)引擎讀取一條記錄
      int error = m_root_iterator->Read();
      DBUG_EXECUTE_IF("bug13822652_1", thd->killed = THD::KILL_QUERY;);

      // 讀取出錯(cuò),直接返回
      if (error > 0 || thd->is_error())  // Fatal error
        return true;
      // error < 0
      // 表示已經(jīng)讀完了所有符合條件的記錄
      // 查詢結(jié)束
      else if (error < 0)
        break;
      // SQL 被客戶端干掉了
      else if (thd->killed)  // Aborted by user
      {
        thd->send_kill_message();
        return true;
      }
      ...
      // 發(fā)送數(shù)據(jù)給客戶端
      if (query_result->send_data(thd, *fields)) {
        return true;
      }
      ...
    }
  }
  ...
}

這個(gè)方法是 select 語(yǔ)句的入口,屬于重量級(jí)方法,在源碼分析的第 1 篇文章《帶你讀 MySQL 源碼:limit, offset》中也介紹過(guò),但是,本文示例 SQL 的執(zhí)行計(jì)劃和之前不一樣,這里有必要再介紹下。

m_root_iterator->Read() 從存儲(chǔ)引擎讀取一條記錄,對(duì)于示例 SQL 來(lái)說(shuō),m_root_iterator 是 FilterIterator 迭代器對(duì)象,實(shí)際執(zhí)行的方法是 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() 方法從存儲(chǔ)引擎讀取一條記錄,因?yàn)槭纠?SQL 中 t1 表的訪問(wèn)方式為全表掃描,所以 m_source 是 TableScanIterator 迭代器對(duì)象。

通過(guò) explain 可以確認(rèn)示例 SQL 中 t1 表的訪問(wèn)方式為全表掃描(type = ALL):

explain select * from t1
where i2 > 20 and (i1 = 50 or i1 = 80)\G

***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | t1
partitions    | <null>
type          | ALL
possible_keys | <null>
key           | <null>
key_len       | <null>
ref           | <null>
rows          | 8
filtered      | 12.5
Extra         | Using where

m_source->Read() 從存儲(chǔ)引擎讀取一條記錄之后,m_condition->val_int() 會(huì)判斷這條記錄是否匹配 where 條件。

m_condition 代表 SQL 的 where 條件,對(duì)于示例 SQL 來(lái)說(shuō),它是 Item_cond_and 對(duì)象。

m_condition->val_int() 實(shí)際執(zhí)行的方法是 Item_cond_and::val_int(),這就是判斷記錄是否匹配示例 SQL where 條件的入口。

compare_int_signed()

// sql/item_cmpfunc.cc
int Arg_comparator::compare_int_signed() {
  // 獲取 where 條件操作符左邊的值
  // 例如:i2 > 20
  // 獲取當(dāng)前讀取記錄的 i2 字段值
  longlong val1 = (*left)->val_int();
  if (current_thd->is_error()) return 0;
  // where 條件操作符左邊的值不為 NULL
  // 才進(jìn)入 if 分支
  if (!(*left)->null_value) {
    // 獲取 where 條件操作符右邊的值
    // 例如:i2 > 20
    // val2 的值就等于 20
    longlong val2 = (*right)->val_int();
    if (current_thd->is_error()) return 0;
    // where 條件操作符右邊的值不為 NULL
    // 才進(jìn)入 if 分支
    if (!(*right)->null_value) {
      // 到這里,where 條件操作符左右兩邊的值都不為 NULL
      // 把 where 條件的 null_value 設(shè)置為 false
      if (set_null) owner->null_value = false;
      // 接下來(lái) 3 行代碼
      // 比較 where 條件操作符左右兩邊的值的大小
      if (val1 < val2) return -1;
      if (val1 == val2) return 0;
      return 1;
    }
  }
  // 如果執(zhí)行到下面這行代碼
  // 說(shuō)明 where 條件操作符左右兩邊的值
  // 至少有一個(gè)是 NULL
  // 把 where 條件的 null_value 設(shè)置為 true
  if (set_null) owner->null_value = true;
  return -1;
}

我們以 id = 2、3 的兩條記錄和示例 SQL 的 where 條件 i2 > 20 為例介紹 compare_int_signed() 的邏輯:

圖片

對(duì)于 where 條件 i2 > 20,longlong val1 = (*left)->val_int() 中的 *left 表示 i2 字段。

讀取 id = 2 的記錄:

i2 字段值為 NULL,if (!(*left)->null_value) 條件不成立,執(zhí)行流程直接來(lái)到 if (set_null) owner->null_value = true,把 where 條件的 null_value 設(shè)置為 true,表示對(duì)于當(dāng)前讀取的記錄,where 條件包含 NULL 值。

然后,return -1,compare_int_signed() 方法執(zhí)行結(jié)束。

讀取 id = 3 記錄:

i2 字段值為 31(即 val1 = 31),if (!(*left)->null_value) 條件成立,執(zhí)行流程進(jìn)入該 if 分支。

對(duì)于 where 條件 i2 > 20,longlong val2 = (*right)->val_int() 中的 *right 表示大于號(hào)右邊的 20(即 val2 = 20),if (!(*right)->null_value) 條件成立,進(jìn)入該 if 分支:

if (set_null) owner->null_value = false,把 where 條件的 null_value 設(shè)置為 false,表示對(duì)于當(dāng)前讀取的記錄,where 條件不包含 NULL 值。

  • if (val1 < val2),val1 = 31 大于 val2 = 20,if 條件不成立。
  • if (val1 == val2),val1 = 31 大于 val2 20,if 條件不成立。
  • return 1,因?yàn)?val1 = 31 大于 val2 = 20,返回 1,表示當(dāng)前讀取的記錄匹配 where 條件 i2 > 20。

Arg_comparator::compare()

// sql/item_cmpfunc.h
inline int compare() { return (this->*func)(); }

Arg_comparator::compare() 只有一行代碼,就是調(diào)用 *func 方法,比較兩個(gè)值的大小。

func 屬性保存了用于比較兩個(gè)值大小的方法的地址,在 Arg_comparator::set_cmp_func(...) 中賦值。

對(duì)于示例 SQL 來(lái)說(shuō),where 條件中的 i1、i2 字段類型都是 int,func 屬性保存的是用于比較兩個(gè)整數(shù)大小的 Arg_comparator::compare_int_signed() 方法的地址。(this->*func)() 調(diào)用的方法就是 Arg_comparator::compare_int_signed()。

Item_func_gt::val_int()

// sql/item_cmpfunc.cc
longlong Item_func_gt::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value > 0 ? 1 : 0;
}

這里調(diào)用的 cmp.compare() 就是上一小節(jié)介紹的 Arg_comparator::compare() 方法。

對(duì)于示例 SQL 來(lái)說(shuō),Arg_comparator::compare() 會(huì)調(diào)用 Arg_comparator::compare_int_signed() 方法,返回值只有 3 種:

  • -1:表示 where 條件操作符左邊的值小于右邊的值。
  • 0:表示 where 條件操作符左邊的值等于右邊的值。
  • 1:表示 where 條件操作符左邊的值大于右邊的值。

我們以 id = 3 的記錄和示例 SQL 的 where 條件 i2 > 20 為例,介紹 Item_func_gt::val_int() 的邏輯:

圖片

i2 字段值為 31,對(duì) where 條件 i2 > 20 調(diào)用 cmp.compare(),得到的返回值為 1(即 value = 1)。

value > 0 ? 1 : 0 表達(dá)式的值為 1,這就是 Item_func_ge::val_int() 的返回值,表示 id = 3 的記錄匹配 where 條件 i2 > 20。

Item_cond_and::val_int()

// sql/item_cmpfunc.cc
longlong Item_cond_and::val_int() {
  assert(fixed == 1);
  // and 連接的 N 個(gè) where 條件都保存到 list 中
  // 根據(jù) list 構(gòu)造迭代器
  List_iterator_fast<Item> li(list);
  Item *item;
  null_value = false;
  // 迭代 where 條件
  while ((item = li++)) {
    if (!item->val_bool()) {
      if (ignore_unknown() || !(null_value = item->null_value))
        return 0;  // return false
    }
    if (current_thd->is_error()) return error_int();
  }
  return null_value ? 0 : 1;
}

Item_cond_and::val_int() 的邏輯:

  • 判斷當(dāng)前讀取的記錄是否匹配 Item_cond_and 對(duì)象所代表的 and 連接的 N 個(gè) where 條件(N >= 2)。
  • 如果對(duì)每個(gè)條件調(diào)用 item->val_bool() 的返回值都是 true,說(shuō)明記錄匹配 and 連接的 N 個(gè) where 條件。
  • 如果對(duì)某一個(gè)或多個(gè)條件調(diào)用 item->val_bool() 的返回值是 false,就說(shuō)明記錄不匹配 and 連接的 N 個(gè) where 條件。

由于 if (ignore_unknown() || !(null_value = item->null_value)) 中的 ignore_unknown() 用于控制 where 條件中包含 NULL 值時(shí)怎么處理,我們需要展開(kāi)介紹 Item_cond_and::val_int() 的代碼。

想要深入了解 Item_cond_and::val_int() 代碼細(xì)節(jié)的讀者朋友,可以做個(gè)心理建設(shè):內(nèi)容有點(diǎn)長(zhǎng)(但不會(huì)太長(zhǎng))。

首先,我們來(lái)看一下 null_value = false:

null_value 的初始值被設(shè)置為 false,表示 and 連接的 N 個(gè) where 條件中,還沒(méi)出現(xiàn)哪個(gè) where 條件包含 NULL 值的情況(畢竟還啥都沒(méi)干)。

null_value 比較重要,它有可能最終決定 Item_cond_and::val_int() 的返回值(后面會(huì)介紹)。

然后,再來(lái)看看 while 循環(huán)的邏輯,這塊內(nèi)容會(huì)有一點(diǎn)點(diǎn)多:

while 循環(huán)迭代 and 連接的 N 個(gè) where 條件。

每迭代一個(gè) where 條件,都調(diào)用 item->val_bool() 方法,判斷當(dāng)前讀取的記錄是否匹配該條件。

如果 val_bool() 的返回值是 true,說(shuō)明記錄匹配該條件,進(jìn)入下一輪循環(huán),迭代下一個(gè) where 條件(如果有的話)。

if (current_thd->is_error()),這行代碼表示執(zhí)行過(guò)程中出現(xiàn)了錯(cuò)誤,我們先忽略它。

如果 val_bool() 的返回值是 false,說(shuō)明記錄不匹配該條件。

接下來(lái)是進(jìn)入下一輪循環(huán),還是執(zhí)行 return 0 結(jié)束 Item_cond_and::val_int() 方法,就要由 if (ignore_unknown() || !(null_value = item->null_value)) 決定了。

展開(kāi)介紹 if (ignore_unknown() || ...) 之前,先來(lái)看看 ignore_unknown() 的定義:

class Item_cond : public Item_bool_func {
  ...
  /// Treat UNKNOWN result like FALSE 
  /// because callers see no difference
  bool ignore_unknown() const { return abort_on_null; }
  ...
}

從代碼注釋可以看到,ignore_unknown() 用于決定是否把 UNKNOWN 當(dāng)作 FALSE 處理。

那么,什么是 UNKNOWN?

在 MySQL 中,NULL 會(huì)被特殊對(duì)待。NULL 和任何值(包含 NULL 本身)通過(guò)關(guān)系操作符(=、>、<、...)比較,得到的結(jié)果都是 NULL,這個(gè)結(jié)果就被認(rèn)為是 UNKNOWN。

如果想知道某個(gè)值是否為 NULL,只能使用 IS NULL、IS NOT NULL 進(jìn)行判斷。

說(shuō)完了 ignore_unknown(),我們回到 if (ignore_unknown() || !(null_value = item->null_value)),它包含兩個(gè)表達(dá)式:

  • ignore_unknown()
  • !(null_value = item->null_value))

如果 ignore_unknown() 的返回值為 true,if 條件成立,執(zhí)行流程就會(huì)進(jìn)入 if 分支,執(zhí)行 return 0,Item_cond_and::val_int() 方法的執(zhí)行流程就此結(jié)束,表示當(dāng)前讀取的記錄不匹配 and 連接的 N 個(gè) where 條件。

如果 ignore_unknown() 的返回值為 false,那么還需要再判斷 !(null_value = item->null_value)) 的值是 true 還是 false。

我們先分解一下 !(null_value = item->null_value)),其中包含 2 個(gè)步驟:

  • null_value = item->null_value
  • !null_value

如果 item->null_value 的值為 false,賦值給 null_value 之后,!null_value 的值為 true,if 條件成立,執(zhí)行流程就會(huì)進(jìn)入 if (ignore_unknown() || ...) 分支,執(zhí)行 return 0,Item_cond_and::val_int() 方法的執(zhí)行流程就此結(jié)束,表示當(dāng)前讀取的記錄不匹配 and 連接的 N 個(gè) where 條件。

item->null_value = false,表示對(duì)于當(dāng)前讀取的記錄,where 條件不包含 NULL 值。

如果 item->null_value 的值為 true,賦值給 null_value 之后,!null_value 的值為 false,即 !(null_value = item->null_value)) 的值為 false,if 條件不成立,執(zhí)行流程不會(huì)進(jìn)入 if (ignore_unknown() || ...) 分支,也就不會(huì)執(zhí)行 return 0 了,接下來(lái)就會(huì)進(jìn)入下一輪循環(huán),迭代下一個(gè) where 條件(如果有的話)。

item->null_value = true,表示對(duì)于當(dāng)前讀取的記錄,where 條件包含 NULL 值。

最后,再來(lái)看看 return null_value ? 0 : 1:

while 循環(huán)迭代完 and 連接的 N 個(gè) where 條件之前,如果 Item_cond_and::val_int() 方法的執(zhí)行流程都沒(méi)有被 while 代碼塊中包含的 return 0 提前結(jié)束,執(zhí)行流程就會(huì)來(lái)到 return null_value ? 0 : 1。

有兩種場(chǎng)景會(huì)導(dǎo)致這種情況的出現(xiàn):

場(chǎng)景 1:

while 循環(huán)迭代 and 連接的 N 個(gè) where 條件的過(guò)程中,對(duì)每個(gè)條件調(diào)用 item->val_bool() 的返回值都是 true。

此時(shí),null_value 屬性的值為 false,null_value ? 0 : 1 表達(dá)式的值為 1,說(shuō)明當(dāng)前讀取的記錄匹配 and 連接的 N 個(gè) where 條件。

場(chǎng)景 2:

while 循環(huán)迭代 and 連接的 N 個(gè) where 條件的過(guò)程中,某個(gè)條件同時(shí)滿足以下 4 個(gè)要求:

調(diào)用 item->val_bool() 的返回值是 false,說(shuō)明當(dāng)前讀取的記錄不匹配該條件。

ignore_unknown() 的返回值也是 false,表示包含 NULL 值的 where 條件的比較結(jié)果(UNKNOWN)不按 false 處理,而是要等到 while 循環(huán)結(jié)束之后,根據(jù) null_value 屬性的值(true 或 false)算總帳。

這是由 Item_cond_and 對(duì)象控制的行為,而不是 and 連接的某個(gè) where 條件控制的行為。

!(null_value = item->null_value)) 表達(dá)式的值為 false,說(shuō)明該條件包含 NULL 值,那么它就是 ignore_unknown() = false 時(shí)需要等到 while 循環(huán)結(jié)束之后,根據(jù) null_value 屬性的值算總帳的條件。

該條件之后的其它 where 條件,不會(huì)導(dǎo)致 while 循環(huán)被提前中止(這樣執(zhí)行流程才能來(lái)到 return null_value ? 0 : 1)。

此時(shí),null_value 屬性的值為 true,null_value ? 0 : 1 表達(dá)式的值為 0,說(shuō)明當(dāng)前讀取的記錄不匹配 and 連接的 N 個(gè) where 條件。

Item_func_eq::val_int()

// sql/item_cmpfunc.cc
longlong Item_func_eq::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value == 0 ? 1 : 0;
}

這里調(diào)用的 cmp.compare() 就是前面介紹的 Arg_comparator::compare() 方法。

對(duì)于示例 SQL 來(lái)說(shuō),Arg_comparator::compare() 調(diào)用的是 Arg_comparator::compare_int_signed() 方法,返回值只有 3 種:

  • -1:表示 where 條件操作符左邊的值小于右邊的值。
  • 0:表示 where 條件操作符左邊的值等于右邊的值。
  • 1:表示 where 條件操作符左邊的值大于右邊的值。

我們以 id = 5 的記錄和示例 SQL 的 where 條件 i1 = 50 為例,介紹 Item_func_eq::val_int() 的邏輯:

圖片

i1 字段值為 50,對(duì) where 條件 i1 = 50 調(diào)用 cmp.compare(),得到的返回值為 0(即 value = 0)。

value == 0 ? 1 : 0 表達(dá)式的值為 1,這就是 Item_func_eq::val_int() 的返回值,表示 id = 5 的記錄匹配 where 條件 i1 = 50。

Item_cond_or::val_int()

// sql/item_cmpfunc.cc
longlong Item_cond_or::val_int() {
  assert(fixed == 1);
  List_iterator_fast<Item> li(list);
  Item *item;
  null_value = false;
  while ((item = li++)) {
    if (item->val_bool()) {
      null_value = false;
      return 1;
    }
    if (item->null_value) null_value = true;
    ...
  }
  return 0;
}

我們以 id = 8 的記錄和示例 SQL 的 where 條件 i1 = 50 or i1 = 80 為例,介紹 Item_cond_or::val_int() 的邏輯:

圖片

Item_cond_or 對(duì)象的 list 屬性包含 2 個(gè)條件:i1 = 50、i1 = 80,List_iterator_fastli(list) 根據(jù) list 構(gòu)造一個(gè)迭代器。

對(duì)于 id = 8 的記錄,i1 字段值為 80,while 循環(huán)每次迭代一個(gè) where 條件:

第 1 次迭代,對(duì) where 條件 i1 = 50 調(diào)用 item->val_bool(),返回值為 false,不進(jìn)入 if (item->val_bool()) 分支。

if (item->null_value) 條件不成立,不執(zhí)行 null_value = true。

第 2 次迭代,對(duì) where 條件 i1 = 80 調(diào)用 item->val_bool(),返回值為 true,進(jìn)入 if (item->val_bool()) 分支。

設(shè)置 Item_cond_or 對(duì)象的 null_value 屬性值為 false,表示 Item_cond_or 所代表的 or 連接的 where 條件(i1 = 50、i1 = 80)都不包含 NULL 值。

return 1,這就是 Item_cond_or::val_int() 的返回值,表示 id = 8 的記錄匹配 where 條件 i1 = 50 or i1 = 80。

總結(jié)

本文介紹了 SQL 的 where 條件中包含 and、or 的實(shí)現(xiàn)邏輯:

從存儲(chǔ)引擎讀取一條記錄之后,對(duì) and 連接的 N 個(gè) where 條件(N >= 2)調(diào)用 item->val_bool() 的返回值必須全部等于 true,記錄才匹配 and 連接的 N 個(gè) where 條件。

Item_cond_and::val_int() 的代碼不多,但是這個(gè)方法中調(diào)用了 ignore_known() 用于控制怎么處理 where 條件包含 NULL 值的場(chǎng)景,代碼細(xì)節(jié)并不太好理解,所以花了比較長(zhǎng)的篇幅介紹 Item_cond_and::val_int() 方法的邏輯,需要多花點(diǎn)時(shí)間去理解其中的邏輯。

從存儲(chǔ)引擎讀取一條記錄之后,對(duì) or 連接的 N 個(gè) where 條件(N >= 2)調(diào)用 item->val_bool(),只要其中一個(gè)返回值等于 true,記錄就匹配 or 連接的 N 個(gè) where 條件。

本文轉(zhuǎn)載自微信公眾號(hào)「一樹(shù)一溪」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹(shù)一溪公眾號(hào)。

責(zé)任編輯:姜華 來(lái)源: 一樹(shù)一溪
相關(guān)推薦

2023-04-17 08:19:47

select *MySQL

2023-04-10 08:07:48

MySQLlimitoffset

2021-09-02 18:36:35

SQLWhereOn

2022-05-30 08:01:36

WHEREMySQL索引

2010-05-18 14:14:03

MySQL關(guān)聯(lián)left

2022-12-14 08:05:56

MySQLORWHERE

2024-12-05 09:45:25

Reactdiff 算法前端開(kāi)發(fā)

2024-09-02 00:00:00

MySQL幻讀數(shù)據(jù)

2022-09-09 19:01:02

接口Reader?Spark

2024-11-18 08:31:03

2025-03-17 08:15:27

SQLJOIN連接

2022-11-05 08:37:00

MySQL數(shù)據(jù)索引

2021-09-16 06:44:07

數(shù)據(jù)庫(kù)SQL語(yǔ)句

2021-02-11 13:30:56

Nodejs源碼c++

2021-07-26 18:23:23

SQL策略優(yōu)化

2019-05-28 13:50:27

MySQL幻讀數(shù)據(jù)庫(kù)

2024-10-05 00:00:10

SQL語(yǔ)句指定連接條

2011-03-25 09:54:39

Oracle數(shù)據(jù)庫(kù)Where條件

2021-01-04 05:53:35

MyBatis底層Java

2022-02-09 07:44:30

Go源碼工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

好吊色欧美一区二区三区视频| 日韩中文字幕第一页| 黄色免费视频大全| 色老头视频在线观看| 国产精品一二一区| 欧美在线日韩在线| 久久av红桃一区二区禁漫| 韩国精品视频| 极品少妇一区二区| 91精品国产精品| av在线免费播放网址| 国产乱论精品| 欧美二区三区91| 国产男女无遮挡| √天堂8在线网| 国产欧美综合色| 国产精品久久亚洲7777| 中文字幕91爱爱| 亚洲精品欧洲| 久久精品国产99国产精品澳门| 三级男人添奶爽爽爽视频 | 国产欧美日韩视频在线观看| 亚洲一区二区免费| 免费黄色一级大片| 在线视频亚洲| 欧美日韩福利视频| 国产一区在线观看免费| 蜜臀av免费一区二区三区| 精品久久久久久久久久久久久久久| 我要看一级黄色大片| 欧美日韩在线观看首页| 一区二区久久久久| 欧美凹凸一区二区三区视频| 亚洲精品久久久蜜桃动漫| 美女尤物国产一区| 国产精品第七十二页| 成年人免费高清视频| 伊人久久亚洲热| 欧美激情女人20p| 中文字幕在线有码| 偷偷www综合久久久久久久| 伊人久久久久久久久久久久久 | aiss精品大尺度系列| 欧美日韩一区二区三区免费看| 日本中文字幕网址| 亚洲精品久久久久久久久久久久久久 | 在线免费看91| 久久一区二区三区超碰国产精品| 国语自产在线不卡| 五月天综合在线| 在线看片一区| 久久久亚洲国产天美传媒修理工| 国产成人无码aa精品一区| 久久的色偷偷| 在线观看91精品国产麻豆| www.com操| 成人精品国产亚洲| 欧美色精品在线视频| 尤蜜粉嫩av国产一区二区三区| 成人欧美大片| 91国偷自产一区二区使用方法| av黄色在线网站| 亚洲最新无码中文字幕久久| 色综合久久久久综合体桃花网| 99精品视频在线看| 在线视频1区2区| 国产日韩欧美精品综合| 婷婷四月色综合| 欧美激情午夜| 91婷婷韩国欧美一区二区| 精品欧美一区二区精品久久| 日韩av成人| 久久久www成人免费无遮挡大片| 欧美精品久久| 99精品老司机免费视频| 亚洲美女少妇撒尿| 国产精品久久久久9999爆乳| 筱崎爱全乳无删减在线观看| 欧美三级日韩三级国产三级| 日本一二三区在线| 欧美黄色录像| 伊人成人开心激情综合网| 777777国产7777777| 91久久视频| 国产精品国产三级国产aⅴ浪潮 | 色女人综合av| 黄网页在线观看| 黄网站色欧美视频| 高清一区在线观看| 日韩激情欧美| 欧美日韩一区视频| 女教师高潮黄又色视频| 色婷婷狠狠五月综合天色拍 | 91精品国产色综合久久ai换脸| 在线播放第一页| 国产欧美一区| 日韩av一区在线| 国产99在线 | 亚洲| 国产一区亚洲| 国产成人精品综合| 亚洲精华国产精华精华液网站| 久久免费看少妇高潮| 色哟哟免费网站| 秋霞午夜在线观看| 五月天激情小说综合| 亚洲一级免费观看| 美女呻吟一区| 久久国产精品久久久久| 国产精品suv一区| 国产福利一区二区三区在线视频| 欧美日韩在线观看一区二区三区| 中文字幕中文字幕在线十八区 | 第四色婷婷基地| 九色91国产| 制服 丝袜 综合 日韩 欧美| 欧美aⅴ99久久黑人专区| 精品福利av导航| 亚洲а∨天堂久久精品2021| 欧美精品一卡| 国产欧美日韩高清| 日韩一级片免费在线观看| 亚洲日本护士毛茸茸| 婷婷丁香激情网| 五月天亚洲色图| 亚洲97在线观看| 亚洲精品久久久久久无码色欲四季| 国产精品久久久久久久久免费相片 | 99在线视频播放| 在线免费观看黄色| 91福利区一区二区三区| 亚洲男女在线观看| 欧美午夜一区| 91青青草免费观看| 久操视频在线免费播放| 在线观看区一区二| 久久av无码精品人妻系列试探| 一区二区三区成人精品| 国产一区二区三区高清| 欧美xxxx性xxxxx高清| 91精品国产免费久久综合| 懂色av蜜臀av粉嫩av永久| 男女男精品视频网| 日韩在线三级| www.26天天久久天堂| 国产一区二区三区在线视频| 91视频在线视频| 中文字幕久久午夜不卡| 日本在线观看免费视频| 日韩精品影视| 成人黄色av网站| a视频在线观看免费| 亚洲狠狠爱一区二区三区| 欧洲美女亚洲激情| 综合亚洲视频| 岛国视频一区免费观看| a天堂资源在线| 日韩成人性视频| 久操视频在线免费观看| 国产精品进线69影院| 午夜免费看毛片| 国语一区二区三区| 91精品91久久久久久| 男人久久精品| 欧美天堂亚洲电影院在线播放| 五月婷婷六月香| 激情图片小说一区| 男人c女人视频| 少妇久久久久| 国产精品一区=区| sm国产在线调教视频| 欧美变态tickle挠乳网站| 国产成人无码一区二区三区在线| 久久婷婷久久一区二区三区| 色悠悠久久综合网| 欧美另类综合| 国产精品初高中精品久久| 无遮挡在线观看| 中文字幕精品在线视频| 国内精品久久久久久久久久| 欧美日韩精品在线| 亚洲一区 欧美| 国产盗摄女厕一区二区三区| heyzo国产| 99国产**精品****| 国产精品二区三区四区| 欧美aa视频| 欧美成人午夜剧场免费观看| 人成免费电影一二三区在线观看| 色综合中文字幕| 51精品免费网站| 91视频一区二区| 日韩精品视频网址| 免费在线亚洲| 美女黄色片网站| 久久不见久久见中文字幕免费| 91精品免费视频| 国产精品粉嫩| 欧美国产日产韩国视频| 国产69精品久久app免费版| 91精品国产91热久久久做人人| 天天操中文字幕| 亚洲黄色在线视频| 懂色av粉嫩av浪潮av| 国产v综合v亚洲欧| 三级av免费观看| 亚洲一区二区毛片| 精品麻豆av| 精品国产一区二区三区性色av | 男女爱爱视频网站| 国产精品手机在线播放| 国产富婆一区二区三区| 亚洲伦理网站| 国产成人精品国内自产拍免费看| 黑人玩欧美人三根一起进 | 男女在线观看视频| 中文字幕久热精品视频在线| 污污视频在线观看网站| 777午夜精品视频在线播放| 成人无码av片在线观看| 成人黄页在线观看| 日韩av影视大全| 美女视频黄久久| www日韩视频| 国产欧美精品久久| 天堂8在线天堂资源bt| 亚洲美女视频| 在线视频不卡一区二区| 国产精品嫩草影院在线看| 国产中文一区二区| 国产精品国产| 国产精品一区二区三区免费| 国产一区二区三区黄网站| 国产精品自拍偷拍视频| jvid一区二区三区| 国产精品久久视频| 日韩成人影音| 国产精品第1页| 久久91导航| 国产精品高清在线观看| 四虎4545www精品视频| 日韩免费高清在线观看| 午夜伦全在线观看| 中文字幕不卡在线视频极品| 日本高清中文字幕二区在线| 国产丝袜一区二区三区| 日本天堂在线| 亚洲网站在线播放| 国产高清自拍视频在线观看| 亚洲视频在线观看免费| 国产农村妇女毛片精品| 欧美丰满美乳xxx高潮www| 国产美女裸体无遮挡免费视频| 欧美日韩国产综合视频在线观看 | 日韩美女一区二区三区四区| 国产情侣av在线| 欧美大片在线观看一区| 亚洲精品字幕在线观看| 精品国产成人在线影院| 天堂成人在线视频| 亚洲精品天天看| 成年人在线观看| 精品国产免费人成在线观看| 亚洲精品国产精品乱码不卡| 亚洲精品国产欧美| 欧美人体大胆444www| 在线电影中文日韩| 免费a级在线播放| 欧美成人精品在线观看| missav|免费高清av在线看| 青青久久aⅴ北条麻妃| 午夜精品成人av| 国产欧美一区二区三区久久人妖 | 欧美一级高清大全免费观看| 国产精品自拍99| 色婷婷亚洲一区二区三区| 最好看的日本字幕mv视频大全| 欧美人狂配大交3d怪物一区| 亚洲成人黄色片| 国产午夜精品久久久| 色影院视频在线| 久久久久久久影院| 欧美大片高清| 亚洲精品欧美日韩| 日韩美脚连裤袜丝袜在线| 午夜精品一区二区三区四区| 99成人超碰| 午夜精品久久久久久久无码| 麻豆中文一区二区| 国产偷人视频免费| 精油按摩中文字幕久久| 国产大学生视频| 日本一区二区三区在线不卡| 免费三片在线播放| 91福利视频久久久久| 亚洲精品久久久久avwww潮水| 亚洲视频在线观看| 免费毛片在线看片免费丝瓜视频| 国产精品爱久久久久久久| 日韩欧美高清一区二区三区| 日本精品一区二区三区不卡无字幕| 欧美a级一区| 97公开免费视频| 国产成人综合视频| 亚洲最大成人网站| 亚洲蜜桃精久久久久久久| 亚洲第一网站在线观看| 日韩欧美二区三区| av在线首页| 欧美在线xxx| 中文久久电影小说| 成人在线看片| 欧美国产一级| 99中文字幕在线观看| 日韩精品国产精品| jlzzjizz在线播放观看| 亚洲精品欧美在线| 亚洲天堂视频网| 亚洲图片欧美午夜| 蜜桃麻豆影像在线观看| 成人黄色片视频网站| 国产精品久久久久无码av| www.四虎成人| 99久久er热在这里只有精品15| 五月天丁香激情| 91精品国产高清一区二区三区| 98在线视频| 国产精品久久久精品| 九九久久婷婷| av7777777| 91麻豆免费看片| 国产精品男女视频| 亚洲精品久久久久中文字幕欢迎你| 蜜臀av国内免费精品久久久夜夜| 91夜夜揉人人捏人人添红杏| 水蜜桃久久夜色精品一区| 99免费视频观看| 国产欧美日韩综合精品一区二区| 欧美精品韩国精品| 亚洲欧美综合精品久久成人| 欧美三级网站| 欧美日韩精品免费看| 久久国产精品99国产| 亚洲av无码一区二区二三区| 精品久久久久久| 欧美日韩国产综合视频| 国产精品第8页| 日本一区二区高清不卡| 在线观看岛国av| 亚洲免费观看在线视频| 国产成人三级一区二区在线观看一| 美女性感视频久久久| 一区视频网站| 色综合久久久久无码专区| 91网站在线观看视频| 中文字幕一区二区人妻视频| 一本色道久久88亚洲综合88| 欧美激情啪啪| wwwjizzjizzcom| 97久久久精品综合88久久| 久久久黄色大片| 中文字幕亚洲欧美一区二区三区| 亚洲国产伊人| 美女黄毛**国产精品啪啪| 美女久久一区| 九九这里只有精品视频| 日韩情涩欧美日韩视频| 国产精品电影| 国产剧情日韩欧美| 羞羞答答成人影院www| 香蕉视频1024| 岛国av在线不卡| 中文字幕在线视频区| 超碰97国产在线| 午夜亚洲影视| 我要看一级黄色录像| 精品捆绑美女sm三区| 中文字幕av一区二区三区佐山爱| 在线亚洲美日韩| www.欧美.com| 在线观看一二三区| 欧美激情aaaa| 青青草国产免费一区二区下载| 在线观看视频你懂得| 欧美日韩国产精品一区二区三区四区 | 无码人妻精品一区二区三区在线 | 日韩成人性视频| 日本成人在线网站| 国产精品成人久久电影| 国产人成一区二区三区影院| 国产三级小视频| 日本精品久久久| 一区二区三区四区在线观看国产日韩| 国产精品果冻传媒| 欧美亚洲一区二区在线观看| 婷婷av在线| 四虎影院一区二区三区| 成人一级黄色片| 亚洲一区二区三区高清视频| 69av成年福利视频| 91精品亚洲|