【GreatSQL优化器-02】索引和Sargable谓词

基于非对称纳什谈判的多微网电能共享运行优化策略(Matlab代码实现) 基于非对称纳什谈判的多微网电能共享运行优化策略(Matlab代码实现)内容概要:本文档标题为《Plecs全桥LLC谐振变换器仿真模型.txt》,但实际上内容并未围绕Plecs或全桥LLC谐振变换器展开详细论述,而是罗列了大量基于Matlab/Simulink和Python的科研仿真项目标题及相关技术方向,涵盖电力系统、无人机控制、微电网优化、路径规划、状态估计、信号处理、电动汽车调度、电力电子变换器等多个领域。其中包括多种优化算法(如粒子群、遗传算法、NSGA-II)、控制策略(如PID、MPC、滑模控制)、仿真建模(如LLC谐振变换器、DAB变换器、PWM整流器)以及具体应用场景的研究实现。尽管提及“隔离型全桥LLC谐振变换器建模及参数设计资料”,但全文缺乏对该主题的具体描述、模型细节或仿真分析,仅作为条目之一出现。因此,该文档实质是一份科研项目资源清单或代码合集目录,而非针对某一主题的深入文章。; 适合人群:具备一定Matlab/Simulink或Python编程基础,从事电气工程、自动化、控制科学、电力电子、新能源系统等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①为科研工作者提供丰富的课题参考与复现资源,涵盖优化调度、控制系统设计、电力电子仿真等方向;②辅助完成硕士/博士论文、期刊论文复现、课程设计或项目开发,提升研究效率与创新性;③通过已有模型和代码快速搭建仿真平台,聚焦算法改进与性能优化。; 阅读建议:此文档应作为科研资源索引使用,读者可根据自身研究方向筛选相关条目,结合配套代码与参考资料进行深入学习与实践。对于希望研究全桥LLC谐振变换器的用户,需注意本文档未提供完整内容,建议查找标注的“仿真+设计报告+计算书”等附件资源以获取详细信息。 立即下载

【GreatSQL优化器-02】索引和Sargable谓词

一、Sargable谓词介绍

GreatSQL的优化器在有过滤条件的时候,需要先把条件按照是否有索引来进行区分,可以用索引来加速查询的条件称为Sargable,其中 arge 来源于 Search Argument(搜索参数)的首字母拼成的"SARG"。GreatSQL用keyuse_array索引数组和Sargables数组来储存Sargable谓词,其中Sargable数组是对keyuse_array的补充使用,比如``a<b``条件如果 a 列上建立有索引并且b不是常量,a存入keyuse_array数组,而这个b就存入Sargable数组。因此 Sargable 也可以叫做 可索引谓词。

Sargable谓词在优化器执行make_join_plan的一开始就通过update_ref_and_keys提取出来了,用于对keyuse_array索引数组没有保存到的索引做补充。

生成的keyuse_array数组在后面的JOIN::optimize_keyuse_array函数里面会给每个索引的ref_table_rows变量赋值:一个索引数据对应多少行表数据,这样在后续Optimize_TABLE_order::find_best_ref计算最佳执行计划表顺序的时候可以根据ref_table_rows变量估计扫描开销,提取到的keyuse_array会让表的扫描方式走ref或者eq_ref方式扫描(见下表<<join_type表扫描方式>>),如果没有keyuse_array的话就是用固定数值进行预估,这样算出来的表的read_cost是不准确的,表的扫描方式走range或者scan方式扫描,这样执行的是全表或索引扫描,这会引起查询的性能下降。

名称说明
Sargable谓词可索引谓词,就是可以使用index的谓词,=, >, <, >=, <=, BETWEEN, IS [NOT] NULL, IN,不以%开头的LIKE
不可索引谓词无法使用索引的谓词,比如NOT EXISTS, NOT LIKE,以%开头的LIKE

下面用一个简单的例子来说明Sargable谓词是什么。

CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT,date1 DATETIME);
INSERT INTO t1 VALUES (1,10,'2021-03-25 16:44:00.123456'),(2,1,'2022-03-26 16:44:00.123456'),(3,4,'2023-03-27 16:44:00.123456');
CREATE TABLE t2 (cc1 INT PRIMARY KEY, cc2 INT);
INSERT INTO t2 VALUES (1,3),(2,1),(3,2),(4,3);
CREATE INDEX idx1 ON t1(c2);
CREATE INDEX idx2 ON t1(c2,date1);
CREATE INDEX idx2_1 ON t2(cc2);

SET optimizer_trace = 'enabled=ON' ;
greatsql> SELECT * FROM t1 where c1 in (SELECT cc1 FROM t2) and  c1 >c2;
+----+------+---------------------+
| c1 | c2   | date1               |
+----+------+---------------------+
|  2 |    1 | 2022-03-26 16:44:00 |
+----+------+---------------------+
> SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
| SELECT * FROM t1 where c1 in (SELECT cc1 FROM t2) and  c1 >c2 | {
  "steps": [
    {// 1、sql语句转换成更快执行的语句
      "join_preparatiON": {
        "SELECT#": 1,
        "steps": [
          {
            "join_preparatiON": {
              "SELECT#": 2,
              "steps": [
                {
                  "expanded_query": "/* SELECT#2 */ SELECT `t2`.`cc1` FROM `t2`"
                }
              ]
            }
          },
          {
            "expanded_query": "/* SELECT#1 */ SELECT `t1`.`c1` AS `c1`,`t1`.`c2` AS `c2`,`t1`.`date1` AS `date1` FROM `t1` where (`t1`.`c1` in (/* SELECT#2 */ SELECT `t2`.`cc1` FROM `t2`) and (`t1`.`c1` > `t1`.`c2`))"
          },
          {
            "transformatiON": {
              "SELECT#": 2,
              "FROM": "IN (SELECT)",
              "to": "semijoin",
              "chosen": true,
              "transformatiON_to_semi_join": {
                "subquery_predicate": "`t1`.`c1` in (/* SELECT#2 */ SELECT `t2`.`cc1` FROM `t2`)",
                "embedded in": "WHERE",
                "semi-join cONditiON": "(`t1`.`c1` = `t2`.`cc1`)",
                "decorrelated_predicates": [
                  {
                    "outer": "`t1`.`c1`",
                    "inner": "`t2`.`cc1`"
                  }
                ]
              }
            }
          },
          {
            "transformatiONs_to_nested_joins": {
              "transformatiONs": [
                "semijoin"
              ],// 这个sql语句被转换成以下最终的语句,可以发现最后以semi join的形式查询的。
              "expanded_query": "/* SELECT#1 */ SELECT `t1`.`c1` AS `c1`,`t1`.`c2` AS `c2`,`t1`.`date1` AS `date1` FROM `t1` semi join (`t2`) where ((`t1`.`c1` > `t1`.`c2`) and (`t1`.`c1` = `t2`.`cc1`))"
            }
          }
        ]
      }
    },
    {// 2、优化器执行计划生成
      "join_optimizatiON": {
        "SELECT#": 1,
        "steps": [
          {
            "cONditiON_processing": {
              "cONditiON": "WHERE",
              "original_cONditiON": "((`t1`.`c1` > `t1`.`c2`) and (`t1`.`c1` = `t2`.`cc1`))",
              "steps": [
                {
                  "transformatiON": "equality_propagatiON",
                  "resulting_cONditiON": "((`t1`.`c1` > `t1`.`c2`) and multiple equal(`t1`.`c1`, `t2`.`cc1`))"
                },
                {
                  "transformatiON": "cONstant_propagatiON",
                  "resulting_cONditiON": "((`t1`.`c1` > `t1`.`c2`) and multiple equal(`t1`.`c1`, `t2`.`cc1`))"
                },
                {
                  "transformatiON": "trivial_cONditiON_removal",
                  "resulting_cONditiON": "((`t1`.`c1` > `t1`.`c2`) and multiple equal(`t1`.`c1`, `t2`.`cc1`))"
                }
              ]
            }
          },
          {
            "substitute_generated_columns": {
            }
          },
          {// 表依赖,因为是两张表做join,因此这里显示了2张表
            "TABLE_dependencies": [
              {
                "TABLE": "`t1`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_ON_map_bits": [
                ]
              },
              {
                "TABLE": "`t2`",
                "row_may_be_null": false,
                "map_bit": 1,
                "depends_ON_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [ //这里就是keyuse_array的结果,实际用到了两个索引,c1和cc2的唯一索引
              {
                "TABLE": "`t1`",
                "field": "c1",
                "equals": "`t2`.`cc1`",
                "null_rejecting": true
              },
              {
                "TABLE": "`t2`",
                "field": "cc1",
                "equals": "`t1`.`c1`",
                "null_rejecting": true
              }
            ]
          },

// 通过查看系统表可以查看到keyuse_array信息,但是Sargables数组信息没有显示。
// 这个通过debug代码发现过程中提取出了一个谓词组,就是c1>c2,field值为cc1列,arg_value值为c2列,num_VALUES为所有Sargable谓词数量,这里为1。
// 这个Sargable数组信息在后面函数update_sargable_FROM_cONst补充检查是否可以使用这里面的cc1索引加速查询。
struct SARGABLE_PARAM {
  Field *field;     // t2的cc1列
  Item **arg_value; // t1的c2列
  uINT num_VALUES;  // 值为1,因为数组只有一个值
};
二、update_ref_and_keys代码执行过程

update_ref_and_keys函数里面通过add_key_fields把查询sql的cond条件包含的索引信息添加到Key_use_array,注意只有等于的条件才会通过add_key_field添加key_field。cond条件分为两种:FUNC_ITEM和COND_ITEM,其中and_level用于在merge_key_fields时候把用不到的key_field删掉。

条件类型说明
FUNC_ITEM只由一个条件组成
COND_ITEM由若干个 AND 和 OR 连接起来的条件,包含Item_cond_or和Item_cond_and两种 COND_AND_FUNC:同一个and条件的and_level不变 COND_OR_FUNC:处理前and_level需要自增

实际代码执行过程:

bool JOIN::make_join_plan() {
  // Build the key access informatiON, which is the basis for ref access.
  //如果有查询条件或者查询语句是outer join的话,需要执行update_ref_and_keys获取所有sargables谓词。
  if (where_cond || query_block->outer_join) {
    if (update_ref_and_keys(thd, keyuse_array, join_tab, TABLEs, where_cONd,
                            ~query_block->outer_join, query_block, &sargables))
      return true;
  }
}

// 这里keyuse_array和sargables都是最后生成的结果,即所需的Sargable谓词
static bool update_ref_and_keys(THD *thd, Key_use_array *keyuse_array,
                                JOIN_TAB *join_tab, uINT TABLEs, Item *cONd,
                                TABLE_map normal_TABLEs,
                                Query_block *query_block,
                                SARGABLE_PARAM **sargables) {
  if (cONd) {
    if (add_key_fields(thd, join, &end, &and_level, cONd, normal_TABLEs,
                       sargables))
      return true;
  }
  /* fill keyuse_array with found key parts */
  // 把上面找到的带有索引的field加入到keyuse_array数组
  for (; field != end; field++) {
    if (add_key_part(keyuse_array, field)) return true;//说明见下面
  }
  if (!keyuse_array->empty()) {
      // 对找到的索引按照sort_keyuse_array条件进行比大小排序
      std::sTABLE_sort(keyuse_array->begin(), keyuse_array->begin() + keyuse_array->size(),
                     sort_keyuse);
     // 删除不用的索引列以及重复的索引列,这里最后装入的就是t1.c1和t2.cc1两个索引列。
  }
}

bool add_key_fields(THD *thd, JOIN *join, Key_field **key_fields,
                    uINT *and_level, Item *cONd, TABLE_map usable_TABLEs,
                    SARGABLE_PARAM **sargables) {
    List_iterator_fast<Item> li(*((Item_cONd *)cONd)->argument_list());
    if (down_cast<Item_cONd *>(cONd)->functype() == Item_func::COND_AND_FUNC) {
      //对AND条件的所有参数进行广度和深度遍历,找出涉及的表的列相关索引,and_level不变,也就是说同一层AND连接的Key_field的and_level相同。
      while ((item = li++)) {
        if (add_key_fields(thd, join, key_fields, and_level, item,
                           usable_TABLEs, sargables))
          return true;
      }
    } else {
      //对OR条件的所有参数进行广度和深度遍历,找出涉及的表的列相关索引,这里多一个merge_key_fields操作,用于对 OR 连接的谓词之间尽可能做 merge 操作
      (*and_level)++;
      add_key_fields();
      merge_key_fields();
    }
  //按照不同函数的不同SELECT_optimize属性来抽取参数涉及的表列,见下表
  auto optimize = cONd_func->SELECT_optimize(thd);
  switch (optimize) {
    case Item_func::OPTIMIZE_NONE:
      break;
    case Item_func::OPTIMIZE_KEY:
    case Item_func::OPTIMIZE_OP:
    case Item_func::OPTIMIZE_NULL:
    case Item_func::OPTIMIZE_EQUAL:
}

static Key_field *merge_key_fields(Key_field *start, Key_field *new_fields,
                                   Key_field *end, uint and_level) {
  for (; new_fields != end; new_fields++) {
    for (Key_field *old = start; old != first_free; old++) {
      //如果当前or条件的item_field等于之前已经标记的条件的item_field
      1、or条件的值不为常量:改变以下3个值
        old->level = and_level;
        old->optimize
        old->null_rejecting
      2、or条件的值等于之前已经标记的条件的值:改变以下3个值
        old->level = and_level;
        old->optimize
        old->null_rejecting
      3、or条件的值等于null,并且之前已经标记的条件的值为null并且为常量:改变以下3个值
        old->level = and_level;
        old->optimize = KEY_OPTIMIZE_REF_OR_NULL;
        old->null_rejecting = false;
      4、以上都不是,那么可以合并,把之前条件的key_field删除。
    }
  }
}

函数的SELECT_optimize属性见下表。

函数的查询优化类型涉及函数对应索引操作
OPTIMIZE_NONE无
OPTIMIZE_KEY<> 、between 、IN函数Item_func::BETWEEN : 把between转换为a>1 and a<10这样的结构,当最小值和最大值相等时,可以创建对应的 Key_field Item_func::MEMBER_OF_FUNC : IN ()寻找索引列 Item_func::IN_FUNC 和 Item_func::NE_FUNC(即 <>): (column1, column2, … ) IN ((const1_1, const1_2), …),存在一个 (column1, column2, …) 上的索引
OPTIMIZE_OPxor,strcmp,nullif,LIKE函数添加EQUAL_FUNC条件的Item包含的列索引
OPTIMIZE_NULLisnull,isnotnull函数column IS [NOT] NULL:对 column IS NULL 才会生成 Key_field,column IS NOT NULL 不是等值表达式,因此不会有对应的 Key_field 生成。
OPTIMIZE_EQUAL连等号,比如x=y=zfield1=field2=…=常量 类型的条件,可以对每个 field=常量 条件生成对应的 Key_field field1=field2=…=fieldn 类型的条件,对于任意两个不同的 field 组成的等值式,尽可能生成 Key_field
三、Key_field举例说明
INSERT INTO t1 VALUES (5,5,'2024-03-25 16:44:00.123456');
INSERT INTO t2 VALUES (5,15);
例子1:SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t2.cc1=t1.c1 or t1.c1=5;
转换为:where (((`t1`.`c1` = `) and (`t2`.`cc1` = `t1`.`c1`)) or (`t1`.`c1` = 5))
对于 t1.c1=t2.cc1 and t2.cc1 = t1.c1 or t1.c1=5
可以生成三个 Key_field:
1. Key_field(c1=cc1, and_level=1)
2. Key_field(cc1=c1, and_level=1)
3. Key_field(c1=5, and_level=2)

做合并之前,先赋值field="cc1=c1"
merge_key_fields()函数执行merge由 OR 连接的左右条件:
->对于 c1=cc1:
        ->判断c1=5是否可以合并
          -> c1相等。可以合并
                  ->将 c1=5 的Key_field删除,剩下c1=cc1,注意这里返回的end="cc1=c1"
->对于 cc1=c1:
        ->判断 c1=5 是否可以合并
          ->cc1不等于c1,不能合并
->将所有没有被合并的 Key_field 去掉

最终剩下2个 Key_field:
Key_field(c1=cc1, and_level=1, optimize=0, null_rejecting=true)
Key_field(cc1=c1, and_level=1, optimize=0, null_rejecting=true)
最后因为通过merge_key_fields算出来的field==end,因此不加入keyuse_array,注意只有or条件才会执行merge_key_fields。这里条件如果去掉or t1.c1=5这两个key_field就会加入keyuse_array

于是看到如下的trace,这里面的access_type全是scan方式,说明没有用索引提升查询性能。
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "TABLE": "`t1`",
                "best_access_path": {
                  "cONsidered_access_paths": [
                    {
                      "rows_to_scan": 4,
                      "filtering_effect": [
                      ],
                      "final_filtering_effect": 1,
                      "access_type": "scan", 这里t1的扫描方式是索引扫描
                      "resulting_rows": 4,
                      "cost": 0.65,
                      "chosen": true
                    }
                  ]
                },
                "rest_of_plan": [
                  {
                    "plan_prefix": [
                      "`t1`"
                    ],
                    "TABLE": "`t2`",
                    "best_access_path": {
                      "cONsidered_access_paths": [
                        {
                          "rows_to_scan": 5,
                          "filtering_effect": [
                          ],
                          "final_filtering_effect": 1,
                          "access_type": "scan", 这里t2的扫描方式是索引扫描
                          "using_join_cache": true,
                          "buffers_needed": 1,
                          "resulting_rows": 5,
                          "cost": 2.25005,
                          "chosen": true
                        }
                      ]
                    },
-- 下面的结果中,type=index,表明选择了索引扫描,跟上面算出来的结论一致
greatsql> EXPLAIN SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t2.cc1=t1.c1 or t1.c1=5;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+---------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra                                                   |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+---------------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | PRIMARY       | idx2   | 11      | NULL |    4 |   100.00 | Using index                                             |   这里选择了索引扫描,跟上面算出来的结论一致
|  1 | SIMPLE      | t2    | NULL       | index | PRIMARY       | idx2_1 | 5       | NULL |    5 |   100.00 | Using where; Using index; Using join buffer (hash join) |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+---------------------------------------------------------+

例子2:SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t1.c2<5;
where条件被转换为:where ((`t1`.`c1` = `t2`.`cc1`) and (`t1`.`c2` < 5))
t1.c2<5不是等于条件,因此不生成Key_field,最后生成2个 Key_field:
1. Key_field(c1=cc1, and_level=0)
2. Key_field(cc1=c1, and_level=0)

因为没有OR条件因此不需要进行合并操作,最终剩下以上2个 Key_field:
Key_field(c1=cc1, and_level=0, optimize=0, null_rejecting=true)
Key_field(cc1=c1, and_level=0, optimize=0, null_rejecting=true)
这两个Key_field会加入keyuse_array,在后面Optimize_TABLE_order::find_best_ref被用于执行优化,让表的扫描方式为eq_ref,见下面。

          {
            "ref_optimizer_key_uses": [ 这里用到了2条包含等号的索引列
              {
                "TABLE": "`t1`",
                "field": "c1",
                "equals": "`t2`.`cc1`",
                "null_rejecting": true
              },
              {
                "TABLE": "`t2`",
                "field": "cc1",
                "equals": "`t1`.`c1`",
                "null_rejecting": true
              }
            ]
          },
            "cONsidered_executiON_plans": [
              {
                "plan_prefix": [
                ],
                "TABLE": "`t1`",
                "best_access_path": {
                  "cONsidered_access_paths": [
                    {
                      "access_type": "ref", 这种ref扫描不被选择
                      "INDEX": "PRIMARY",
                      "usable": false,
                      "chosen": false
                    },
                    {
                      "rows_to_scan": 3,
                      "filtering_effect": [
                      ],
                      "final_filtering_effect": 1,
                      "access_type": "range", 第一次需要对于t1做范围扫描
                      "range_details": {
                        "used_INDEX": "PRIMARY"
                      },
                      "resulting_rows": 3,
                      "cost": 0.860732,
                      "chosen": true
                    }
                  ]
                "rest_of_plan": [
                  {
                    "plan_prefix": [
                      "`t1`"
                    ],
                    "TABLE": "`t2`",
                    "best_access_path": {
                      "cONsidered_access_paths": [
                        {
                          "access_type": "eq_ref", 对于t2.cc1=t1.c1条件,因为t1.c1固定了因此这里t2的扫描方式可以用eq_ref的方式
                          "INDEX": "PRIMARY",
                          "rows": 1,
                          "cost": 1.05,
                          "chosen": true,
                          "cause": "clustered_pk_chosen_by_heuristics"
                        },
                        {
                          "access_type": "range", 这种范围扫描不被选择
                          "range_details": {
                            "used_INDEX": "PRIMARY"
                          },
                          "chosen": false,
                          "cause": "heuristic_INDEX_cheaper"
                        }
                      ]
                    },
                    "cONditiON_filtering_pct": 100,
                    "rows_for_plan": 3,
                    "cost_for_plan": 1.91073,
                    "chosen": true
                  }
                ]
              {
                "plan_prefix": [
                ],
                "TABLE": "`t2`",
                "best_access_path": {
                  "cONsidered_access_paths": [
                    {
                      "access_type": "ref", 这种ref扫描不被选择
                      "INDEX": "PRIMARY",
                      "usable": false,
                      "chosen": false
                    },
                    {
                      "rows_to_scan": 4,
                      "filtering_effect": [
                      ],
                      "final_filtering_effect": 1,
                      "access_type": "range", 对于t2表用到了范围扫描
                      "range_details": {
                        "used_INDEX": "PRIMARY"
                      },
                      "resulting_rows": 4,
                      "cost": 1.06082,
                      "chosen": true
                    }
                  ]
                },
                "rest_of_plan": [
                  {
                    "plan_prefix": [
                      "`t2`"
                    ],
                    "TABLE": "`t1`",
                    "best_access_path": {
                      "cONsidered_access_paths": [
                        {
                          "access_type": "eq_ref", 对于t1.c1=t2.cc1条件,因为t2.cc1固定了因此这里t1的扫描方式可以用eq_ref的方式
                          "INDEX": "PRIMARY",
                          "rows": 1,
                          "cost": 1.4,
                          "chosen": true,
                          "cause": "clustered_pk_chosen_by_heuristics"
                        },
                        {
                          "access_type": "range", 这种范围扫描不被选择
                          "range_details": {
                            "used_INDEX": "PRIMARY"
                          },
                          "chosen": false,
                          "cause": "heuristic_INDEX_cheaper"
                        }
                      ]
                    },
-- t1表:type=range,这个扫描方式,下一期讲
-- t2表:type=eq_ref方式扫描
greatsql> EXPLAIN SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t1.c1<5;
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | range  | PRIMARY       | PRIMARY | 4       | NULL      |    3 |   100.00 | Using where |
|  1 | SIMPLE      | t2    | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | db1.t1.c1 |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+

从上面的例子发现有唯一索引查询的时候,条件带有or的话是无法使用Sargable谓词提高效率的。如果是and条件的话,只有等于条件才会被判定为Sargable谓词。因此做join连接的时候尽量避免or条件的过滤。

-- 对比上面2个最后生成的执行计划,第一个预估cost=2.90,第二个预估cost=1.91,效率更高
greatsql> EXPLAIN FORMAT=TREE SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t2.cc1=t1.c1 or t1.c1=5;
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                   |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: ((t2.cc1 = t1.c1) or (t1.c1 = 5))  (cost=2.90 rows=20)
    -> Inner hash join (no cONditiON)  (cost=2.90 rows=20)
        -> INDEX scan ON t2 using idx2_1  (cost=0.19 rows=5)
        -> Hash
            -> INDEX scan ON t1 using idx2  (cost=0.65 rows=4)
 |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

greatsql> EXPLAIN FORMAT=TREE SELECT * FROM t1 join t2 ON t1.c1=t2.cc1 and t1.c1<5;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                            |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Nested loop inner join  (cost=1.91 rows=3)
    -> Filter: (t1.c1 < 5)  (cost=0.86 rows=3)
        -> INDEX range scan ON t1 using PRIMARY over (c1 < 5)  (cost=0.86 rows=3)
    -> Single-row INDEX lookup ON t2 using PRIMARY (cc1=t1.c1)  (cost=0.28 rows=1)
 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

附录:join_type表扫描方式

JT_UNKNOWN
JT_SYSTEM表只有一行,比如SELECT * FROM (SELECT 1)
JT_CONST表最多只有一行满足,比如WHERE TABLE.pk = 3
JT_EQ_REF=符号用在唯一索引
JT_REF=符号用在非唯一索引
JT_ALL全表扫描
JT_RANGE范围扫描
JT_INDEX_SCAN索引扫描
JT_FTFulltext索引扫描
JT_REF_OR_NULL包含null值,比如"WHERE col = ... OR col IS NULL
JT_INDEX_MERGE一张表执行多次范围扫描最后合并结果
四、总结

从上面优化器最早的步骤我们认识了Sargable谓词的定义和判定方法,如果查询用到了Sargable谓词是可以进行eq_ref扫描方式的,有效提高了查询效率。通过实际例子发现,在做多表连接的时候用OR条件会降低执行效率,同时用唯一索引列作为连接条件的话会提高效率。因此实际写查询sql的时候,尽量用唯一索引作为连接条件,少用OR条件进行过滤。

Enjoy GreatSQL :)

关于 GreatSQL

GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

image

社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html

社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html

(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)

技术交流群:

微信&QQ群:

QQ群:533341697

微信群:添加GreatSQL社区助手(微信号:wanlidbc )好友,待社区助手拉您进群。

高校技术转移办公室在推动成果转化过程中,如何高效完成技术成果的信息整合与价值挖掘.docx 科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。 立即下载

相关推荐

高校技术转移办公室人员,如何精准挖掘并分析企业的技术需求?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

什么是Sargable?

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33720956的博客 456

芯片硅前验证技术与驱动工程师实战指南

硅前验证是芯片开发流程中的关键环节,通过pre-silicon simulation等技术手段在流片前验证设计正确性。其核心技术包括软件仿真、FPGA原型验证和硬件仿真器解决方案,涉及Linux驱动开发、芯片架构理解和波形分析等核心技能。作为连接芯片设计与系统集成的桥梁,硅前验证能早期发现设计缺陷,节省数百万美元流片成本。以Cadence Palladium为代表的硬件仿真器,通过专用服务器系统实现比纯软件方案快100-1000倍的仿真速度,为驱动工程师提供完整的外设模型验证环境。掌握硅前验证技术可显著提升

weixin_42522857的博客 323

《Pro SQL Server Internals, 2nd edition》的CHAPTER 2 Tables and Indexes中的Clustered Indexes的翻译

来源:《Pro SQL Server Internals, 2nd edition》的CHAPTER 2 Tables and Indexes中的Clustered Indexes的翻译 聚集索引 聚集索引指示表中数据的物理顺序,表中数据按照聚集索引进行排序索引键。表只能定义一个聚集索引。 让我们假设您希望在堆表上使用数据创建一个聚集索引。作为第一步,如图2-5所示,SQL Server创建...

xin_85的博客 199

全志T113 Tina5.0开发:(一)添加自己的板级支持包

3.验证 先执行 source build/envsetup.sh ,然后lunch,下图可见9就是我们自己添加的板子。2. 1)修改 t113-evb1.mk重命名为t113-ws_devboard。我使用的spinand,只需要修改openwrt里面的文件即可。以下内容仅作为自己笔记,如果错误欢迎留言指出。修改t113-ws_devboard。

u014428915的博客 1164

ThingsBoard入门实战(二):ThingsBoard 设备接入

文章目录ThingsBoard 设备接入一、设备配置/设备类型二、设备三、设备接入HTTPCOAPMQTT四、下一步 ThingsBoard 设备接入 设备接入并不一定需要真正的设备,我们可以通过程序。或者命令来模拟设备,向平台发送信息。 这节我们先为用户分配一个设备,然后通过模拟设备命令更新设备的状态(遥测值)。 一、设备配置/设备类型 添加一个叫"路灯"的设备配置。 设备配置听起来比较抽象,可以认为是设备类型或者是设备类型的配置。实际上,ThingsBoard 中很多时候让选择设备类型,就是选的这个。

AIGC搞起 1万+

Sargable 与 谓语下推 (predicate pushdown) 简介

关键词:SQL优化 , sargable , pushdown filter , predicate pushdown Sargable Sargable = Search ARGument ABLE ,即SQL中可利用数据库自身索引优势对查询条件进行执行性能优化。换句话说,即可以利用存储层的索引优势来优化的查询条件。wikipedia: https://en.wikipedia.org...

weixin_30521161的博客 390

查看电脑mac地址+主机名+用户名并导出

张三:李四,应公司要求,今天下班前必须将这张表格里面用户电脑的mac地址统计出来!李四:我该怎么办呢?一个一个查看,太没有效率了吧!对了,我可以做一个bat脚本啊!

拥抱着青春 1974

SARGable predicate 是什么?

‘SARGable’ is a weird term. It gets bandied around a lot when talking about indexes and whether queries can seek on indexes. The term’s an abbreviation, ‘SARG’ stands for Search ARGument, and it means that the predicate can be executed using an index seek.

houzhizhen的专栏 321

【破局之道】LangChain4j 0.31.0 + Java 8 + Spring Boot:在“过时”环境中构建本地大模型应用的实战指南

本文为使用Java 8和Spring Boot等“过时”技术栈的团队提供了构建本地大模型应用的实战指南。通过采用LangChain4j 0.31.0这一兼容版本,结合Ollama部署本地大模型,开发者无需升级JDK即可在现有项目中快速集成AI能力,实现智能问答、文档摘要等功能,为传统项目注入创新活力。

t4y5u6i7o的博客 557

用Matlab机器人工具箱搞定六轴机械臂建模:从DH参数到3D可视化(附完整代码)

本文详细介绍了如何使用Matlab机器人工具箱进行六轴机械臂建模,从DH参数输入到3D可视化全流程解析。通过正运动学验证、逆运动学求解和交互控制等实战技巧,帮助工程师快速掌握机械臂建模核心方法,并提供完整代码示例解决常见问题。

weixin_27372101的博客 394

mysql 优化器关联查询_MySQL 查询优化器(二)

1.6多个查询字段(常量条件)多个查询字段的查询处理逻辑如下所示:JOIN:prepare阶段setup_tables():同1.1测试。setup_fields():同1.1测试。setup_conds():同1.4测试。JOIN:optimize阶段optimize_cond():类似1.4测试,不同之处在于查询的where条件中有恒等常量,在优化过程中会调用remove_eq_conds将1...

weixin_39595271的博客 358

YOLOv11的元学习(MAML)few-shot适配-(5样本内快速适应新类别)

本文提出了一种基于元学习和YOLOv11的少样本目标检测方法。首先介绍了元学习的核心概念和MAML算法原理,分析了YOLOv11的架构改进及其在少样本检测中的适用性。针对少样本检测面临的过拟合、特征混淆和定位困难等挑战,提出了基于MAML的解决方案。在实现方面,详细阐述了环境配置、数据预处理和任务采样策略,构建了支持N-way K-shot任务的数据加载器。实验表明,该方法相比传统微调策略可显著提升小样本场景下的检测性能。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 601

《Pro SQL Server Internals, 2nd edition》的CHAPTER 2 Tables and Indexes中的Clustered Indexes一节...

聚集索引 聚集索引指示表中数据的物理顺序, 表中的数据根据聚集索引键进行排序。表只能定义一个聚集索引。 假设您要使用数据在堆表上创建聚集索引。作为第一步 (如图2-5 所示), sql server 将创建数据的另一个副本, 然后根据群集密钥的值进行排序。数据页链接在一个双链接列表中, 其中每个页面都包含指向链中下一页和上一页的指针。此列表称为索引的叶级别, 它包含实际的表数据。 图2...

weixin_30341735的博客 188

软考嵌入式 第五讲 嵌入式Linux系统技术

5.1 inittab配置文件 Linux实时操作系统开机引导时首先启动内核,由内核检查和初始化硬件设备,载入设备的驱动程序模块,安装root文件系统,然后内核将启动一个名为init的进程,它是系统运行的第一个进程,进程号为1。在init运行完成并启动其他必要的后续进程后,系统开始运行,引导过程结束。 在任何时候,可以在文件inittab中添加新的记录项。重新检查inittab文件时,可以通过命令:init q/ Q使init进程立即重新读取并处理文件inittab。 5.1.1 inittab文件记录

Leon的博客 526

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

表和索引:内部结构和访问方法(第9周翻译)

图 大量转发指针导致额外的I/O操作,并显著降低了访问数据的查询的性能。本书的配套材料包括在大范围内用包含大量数据的表演示这个问题的脚本。 当转发行的大小被另一次更新减少,并且带有转发指针的数据页有足够的空间容纳更新后的行版本时,SQL Server可能会将其移动回原来的数据页并删除转发指针行。然而,摆脱所有转发指针的唯一可靠方法是重新构建堆表。您可以通过使用ALTER T...

xtt_3170707038的博客 354

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

聚集索引(翻译)

【翻译自】 《Pro SQL Server Internals,2nd edition》 CHAPTER 2 Tables and Indexes Clustered Indexes 聚集索引指示表中数据的物理顺序,表根据聚集索引键进行排序。 每个表只能定义一个聚集索引。 假设您要在堆表上使用数据创建聚集索引。 第一步,如图2-5所示,SQL Server会创建另一个数据副本,然后根据群集密钥的值...

Suqing49的博客 366

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

mysql 索引文件 源码_MySQL源码:索引相关的数据结构(后篇)

前篇介绍了MySQL存储索引信息的基本数据结构。本篇将延续下去,介绍MySQL如何找到可以使用的索引,以及期间需要使用的主要数据结构。谁适合阅读: 本文不打算从High Level来介绍MySQL索引及其使用,相反是从MySQL源码对应的数据结构开始介绍。如果你了解MySQL索引的基本原理,还打算继续从源码的角度解决一些索引使用的问题,那么你适合参考本文,否则,打住,真的很枯燥:(。在可见的未来,...

weixin_32661017的博客 329

SQL进阶之旅 Day 2:基础查询优化技巧

今天我们深入探讨了SQL基础查询优化的两大核心领域:WHERE条件优化和JOIN优化基础。SARGable条件的重要性及其编写技巧不同JOIN算法的适用场景和性能差异执行计划的解读与分析方法实际工作中的优化案例提升现有系统的查询性能优化数据仓库的ETL过程改善Web应用的数据库响应速度明天我们将进入索引基础与应用的学习,敬请期待!编写高效的WHERE条件以减少扫描行数理解不同JOIN算法的工作原理和适用场景分析执行计划以识别性能瓶颈实际工作中的查询优化技巧。

在未来等你的专栏 1241

DB2数据库查询过程(Query Processing)----概述

引言 我们知道,目前通用的数据库查询语言是SQL语言(Structured Query Language)。SQL语言也是一种编译型语言,需要SQL编译器编译后才能执行,但它与C、C++、Java等语言不同,SQL语言是一种非过程化语言,这意味着使用SQL进行操作的时候,你只需要指定你要达到什么目的,而无需指明要怎样达到目的。比如要查询EMPLOYEE的所有行,使用语句“Select * Fro...

weixin_30666401的博客 705

SQL Server 性能调优4 之书写高效的查询

限制查询的行和列来提高性能 这条规则非常简单,这里就不细说了。 使用搜索可参数化判断(sargable conditions)来提高性能 Sargable 由 Search ARGument Able 简写而来,字面意思是搜索可参数化?还是比较晦涩哎... 总之使用Sargable判断可以帮助查询优化器更有效地利用索引,并提高采用 index seek 的可能性,我们先把所有的操作符分

sqlchen的专栏 1698

MySQL 查询优化器(三)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34117522的博客 181

mysql join大小表顺讯_MySQL优化器join顺序

前一篇介绍了cost的计算方法,下面测试一下两表关联的查询:测试用例CREATE TABLE `xpchild` (`id` int(11) NOT NULL,`name` varchar(100) DEFAULT NULL,`c1` int(11) DEFAULT NULL,`c2` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `xpchild_nam...

weixin_42313675的博客 406

MySQL优化器cost计算

记录MySQL 5.5上,优化器进行cost计算的方法。 第一篇: 单表的cost计算 数据结构: 1. table_share: 包含了表的元数据,其中索引部分: key_info:一个key的结构体,代表一个索引,包含了: key_length:key的长度 key_parts:key一共有多少个column key_part:key中具体的column rec_per_key...

qq_40026782的博客 1160

MySQL源码:索引相关的数据结构(后篇)

0. 概述 本文介绍MySQL如何发现WHERE条件中的等值表达式,并通过分析这些等值表达式,找到可以使用的索引。在这个过程中,MySQL将递归的访问所有WHERE条件"谓词",并将等值表达式都存储到KEY_FIELD对象的数组中。 然后遍历该KEY_FIELD数组,并同时对比所有索引列,找到哪些字段是在索引列中出现,这些字段则可能可以使用索引,MySQ...

weixin_33979745的博客 256
上一篇: 5.7 与 8.0 对相同文件的 LOAD DATA 语句结果不同
下一篇: 【GreatSQL优化器-03】查询开销估算
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值