If subqueries are not expensive (5.2, or changed 5.3) the query is optimized as follows:
- JOIN::optimize for the outer query calls:
having= optimize_cond(this, having, join_list, &having_value, &having_equal);
- optimize_cond() calls remove_eq_conds()
- remove_eq_conds() calls eval_const_cond() as follows:
else if (cond->const_item() && !cond->is_expensive())
{
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
return (COND*) 0;
}
- eval_const_cond() calls Item_in_optimizer::val_int(), which in turn
optimizes and executes the subquery, and returns FALSE to remove_eq_conds().
- remove_eq_conds() returns a NULL item, and sets JOIN::having_value = Item::COND_FALSE.
- JOIN::optimize checks that having_value is Item::COND_FALSE, and sets
zero_result_cause= "Impossible HAVING"
- JOIN::exec checks for zero_result_cause and returns empty result without executing.
EXPLAIN shows "Impossible HAVING".
If subqueries are expensive (5.3), the query is processed as follows:
- optimize_cond for the outer JOIN doesn't remove the HAVING clause.
- The optimizer optimizes both the query and the subquery.
- Execution proceeds normally by executing the three-way join in the
query until end_send_group evaluates the HAVING clause once, which
results in a single subquery execution.
Since the subquery is evaluated only once, one may wonder why this query
takes so much time. The reason is an inefficient JOIN plan that is chosen
with the default join_cache_level=2. The plan is:
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------+
| 1 | PRIMARY | alias3 | index | NULL | a | 19 | NULL | 133 | 100.00 | Using index |
| 1 | PRIMARY | alias2 | index | a | a | 19 | NULL | 133 | 100.00 | Using index; Using join buffer (flat, BNL join) |
| 1 | PRIMARY | alias1 | index | a | a | 19 | NULL | 133 | 100.00 | Using where; Using index; Using join buffer (flat, BNL join) |
| 2 | DEPENDENT SUBQUERY | t1 | index_subquery | a | a | 19 | const | 10 | 100.00 | Using index; Using where |
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------------------------------------------+
This plan takes 1.8 sec.
With join_cache_level=0 there is a much better plan:
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------+
| 1 | PRIMARY | alias1 | index | a | a | 19 | NULL | 133 | 100.00 | Using index |
| 1 | PRIMARY | alias2 | index | a | a | 19 | NULL | 133 | 100.00 | Using where; Using index |
| 1 | PRIMARY | alias3 | index | NULL | a | 19 | NULL | 133 | 100.00 | Using index |
| 2 | DEPENDENT SUBQUERY | t1 | index_subquery | a | a | 19 | const | 10 | 100.00 | Using index; Using where |
+----+--------------------+--------+----------------+---------------+------+---------+-------+------+----------+--------------------------+
This plan takes 0.07 sec.
If we allow subqueries to be used for constant optimization we mask the problem
with the inneficient JOIN plan.
If subqueries are not expensive (5.2, or changed 5.3) the query is optimized as follows:
- JOIN::optimize for the outer query calls: is_expensive( )) cond(cond) ? Item::COND_TRUE : Item::COND_FALSE; optimizer: :val_int( ), which in turn result_ cause= "Impossible HAVING"
having= optimize_cond(this, having, join_list, &having_value, &having_equal);
- optimize_cond() calls remove_eq_conds()
- remove_eq_conds() calls eval_const_cond() as follows:
else if (cond->const_item() && !cond->
{
*cond_value= eval_const_
return (COND*) 0;
}
- eval_const_cond() calls Item_in_
optimizes and executes the subquery, and returns FALSE to remove_eq_conds().
- remove_eq_conds() returns a NULL item, and sets JOIN::having_value = Item::COND_FALSE.
- JOIN::optimize checks that having_value is Item::COND_FALSE, and sets
zero_
- JOIN::exec checks for zero_result_cause and returns empty result without executing.
EXPLAIN shows "Impossible HAVING".
If subqueries are expensive (5.3), the query is processed as follows:
- optimize_cond for the outer JOIN doesn't remove the HAVING clause.
- The optimizer optimizes both the query and the subquery.
- Execution proceeds normally by executing the three-way join in the
query until end_send_group evaluates the HAVING clause once, which
results in a single subquery execution.
Since the subquery is evaluated only once, one may wonder why this query ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- ------- ------- ------- ------- ------- ------+ ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- ------- ------- ------- ------- ------- ------+ ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- ------- ------- ------- ------- ------- ------+
takes so much time. The reason is an inefficient JOIN plan that is chosen
with the default join_cache_level=2. The plan is:
+----+-
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-
| 1 | PRIMARY | alias3 | index | NULL | a | 19 | NULL | 133 | 100.00 | Using index |
| 1 | PRIMARY | alias2 | index | a | a | 19 | NULL | 133 | 100.00 | Using index; Using join buffer (flat, BNL join) |
| 1 | PRIMARY | alias1 | index | a | a | 19 | NULL | 133 | 100.00 | Using where; Using index; Using join buffer (flat, BNL join) |
| 2 | DEPENDENT SUBQUERY | t1 | index_subquery | a | a | 19 | const | 10 | 100.00 | Using index; Using where |
+----+-
This plan takes 1.8 sec.
With join_cache_level=0 there is a much better plan: ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- -----+ ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- -----+ ------- ------- -----+- ------- +------ ------- ---+--- ------- -----+- -----+- ------- -+----- --+---- --+---- ------+ ------- ------- ------- -----+
+----+-
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-
| 1 | PRIMARY | alias1 | index | a | a | 19 | NULL | 133 | 100.00 | Using index |
| 1 | PRIMARY | alias2 | index | a | a | 19 | NULL | 133 | 100.00 | Using where; Using index |
| 1 | PRIMARY | alias3 | index | NULL | a | 19 | NULL | 133 | 100.00 | Using index |
| 2 | DEPENDENT SUBQUERY | t1 | index_subquery | a | a | 19 | const | 10 | 100.00 | Using index; Using where |
+----+-
This plan takes 0.07 sec.
If we allow subqueries to be used for constant optimization we mask the problem
with the inneficient JOIN plan.