飞利浦·斯塔克|庖丁解牛|图解 MySQL 8.0 优化器查询转换篇( 二 )


EXISTS , 必须没有聚合 Subquery谓词在WHERE子句(目前没有在ON子句实现) , 而且是ANDs or ORs的表达式tree 父查询块支持semijoins 子查询的策略还没有指定Subquery_strategy::UNSPECIFIED 父查询也至少有一个表 , 然后可以做LEFT JOIN 父查询块不禁止semijoin IN谓词返回值是否是确定的 , 不是RAND 根据子查询判断结果是否需要转成true还是false以及是否为NULL , 判断是可以做antijoin还是semijoin 不支持左边参数不是multi-column子查询(WHERE (outer_subq) = ROW(derived.col1derived.col2)) 该子查询不支持转换为Derived table(m_subquery_to_derived_is_impossible) 设置Subquery_strategy::CANDIDATE_FOR_DERIVED_TABLE并添加sj_candidates 如果上面两个策略无法使用 , 根据类型选择transformer Item_singlerow_subselect::select_transformer 对于简单的标量子查询 , 在查询中直接用执行结果代替 select * from t1 where a = (select 1); =select * from t1 where a = 1; 【飞利浦·斯塔克|庖丁解牛|图解 MySQL 8.0 优化器查询转换篇】
Item_in_subselect/Item_allany_subselect::select_transformer-select_in_like_transformer select_in_like_transformer函数来处理 IN/ALL/ANY/SOME子查询转换transformation 处理\"SELECT 1\"(Item_in_optimizer) 如果目前还没有子查询的执行方式 , 也就是无法使用semijoin/antijoin执行的子查询 , 会做IN-EXISTS的转换 , 本质是在物化执行和迭代式循环执行中做选择 。 IN语法代表非相关子查询仅执行一次 , 将查询结果物化成临时表 , 之后需要结果时候就去物化表中查找;EXISTS代表对于外表的每一条记录 , 子查询都会执行一次 , 是迭代式循环执行 。 子查询策略设定为Subquery_strategy::CANDIDATE_FOR_IN2EXISTS_OR_MAT 重写single-column的IN/ALL/ANY子查询(single_value_transformer) oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)=- oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect- oe $cmp$ \\max\\(SELECT ...) // handled by Item_maxmin_subselectfails=Item_in_optimizer- 对于已经是materialized方案 , 不转换- 通过equi-join转换IN到EXISTS 如果是ALL/ANY单值subquery谓词 , 尝试用MIN/MAX子查询转换 SELECT * FROM t1 WHERE aANY (SELECT a FROM t1); =SELECT * FROM t1 WHERE a(SELECT MAX(a) FROM t1)
不满足上面 , 调用single_value_in_to_exists_transformer转换IN到EXISTS 转换将要将子查询设置为相关子查询 , 设置UNCACHEABLE_DEPENDENT标识 如果子查询包含聚合函数、窗口函数、GROUP语法、HAVING语法 , 将判断条件加入到HAVING子句中 , 另外通过ref_or_null_helper来区分NULL和False的结果 , 如需要处理NULL IN (SELECT ...)还需要封装到Item_func_trig_cond触发器中 。SELECT ... FROM t1 WHERE t1.b IN (SELECTexpr of SUM(t1.a)FROM t2)=SELECT ... FROM t1 WHERE t1.b IN (SELECTexpr of SUM(t1.a)FROM t2 [trigcond
HAVING t1.b=ref-to-expr of SUM(t1.a))
如果子查询不包含聚合函数、窗口函数、GROUP语法 , 会放在WHERE查询条件中 , 当然如果需要处理NULL情况还是要放入HAVING子句(Item_func_trig_cond+Item_is_not_null_test) 。不需要区分NULL和FALSE的子查询:SELECT 1 FROM ... WHERE (oe $cmp$ ie) AND subq_where需要区分的子查询:SELECT 1 FROM ... WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL)) HAVING trigcond(@is_not_null_test@(ie)) JOIN::optimize()会计算materialization和EXISTS转换的代价进行选择 , 设置m_subquery_to_derived_is_impossible = true ROW值转换 , 通过Item_in_optimizer , 不支持ALL/ANY/SOME(row_value_transformer) Item_in_subselect::row_value_in_to_exists_transformer for (each left operand) create the equi-join condition if (is_having_used || !abort_on_null) create the \"is null\" and is_not_null_test items if (is_having_used) add the equi-join and the null tests to HAVING else add the equi-join and the \"is null\" to WHERE add the is_not_null_test to HAVING 没有HAVING表达式 (l1 l2 l3) IN (SELECT v1 v2 v3 ... WHERE where) =EXISTS (SELECT ... WHERE where and (l1 = v1 or is null v1) and (l2 = v2 or is null v2) and (l3 = v3 or is null v3) [ HAVING is_not_null_test(v1) and is_not_null_test(v2) and is_not_null_test(v3))