Commit 554278e2 authored by Igor Babaev's avatar Igor Babaev

MDEV-7487 Semi-join optimization for single-table update/delete statements

This patch allows to use semi-join optimization at the top level of
single-table update and delete statements.
The problem of supporting such optimization became easy to resolve after
processing a single-table update/delete statement started using JOIN
structure. This allowed to use JOIN::prepare() not only for multi-table
updates/deletes but for single-table ones as well. This was done in the
patch for mdev-28883:
Re-design the upper level of handling UPDATE and DELETE statements.

Note that JOIN::prepare() detects all subqueries that can be considered
as candidates for semi-join optimization. The code added by this patch
looks for such candidates at the top level and if such candidates are found
in the processed single-table update/delete the statement is handled in
the same way as a multi-table update/delete.

    Approved by Oleksandr Byelkin <sanja@mariadb.com>
parent e2e3524d
This diff is collapsed.
This diff is collapsed.
...@@ -243,7 +243,7 @@ rows_examined sql_text ...@@ -243,7 +243,7 @@ rows_examined sql_text
4 UPDATE t1 SET a=a+sleep(.02) WHERE a>2 4 UPDATE t1 SET a=a+sleep(.02) WHERE a>2
8 UPDATE t1 SET a=a+sleep(.02) ORDER BY a DESC 8 UPDATE t1 SET a=a+sleep(.02) ORDER BY a DESC
1 UPDATE t2 set b=b+sleep(.02) limit 1 1 UPDATE t2 set b=b+sleep(.02) limit 1
4 UPDATE t1 SET a=a+sleep(.02) WHERE a in (SELECT b from t2) 10 UPDATE t1 SET a=a+sleep(.02) WHERE a in (SELECT b from t2)
6 DELETE FROM t1 WHERE a=a+sleep(.02) ORDER BY a LIMIT 2 6 DELETE FROM t1 WHERE a=a+sleep(.02) ORDER BY a LIMIT 2
disconnect con2; disconnect con2;
connection default; connection default;
......
...@@ -218,14 +218,16 @@ INSERT INTO t2 VALUES (1), (2), (3); ...@@ -218,14 +218,16 @@ INSERT INTO t2 VALUES (1), (2), (3);
# #
EXPLAIN UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); EXPLAIN UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 Using where
FLUSH STATUS; FLUSH STATUS;
FLUSH TABLES; FLUSH TABLES;
EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3); EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE 1 IN (SELECT 1 FROM t2 WHERE t2.b < 3);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00
2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00 Using where
# Status of EXPLAIN EXTENDED query # Status of EXPLAIN EXTENDED query
Variable_name Value Variable_name Value
Handler_read_key 4 Handler_read_key 4
...@@ -246,8 +248,9 @@ Handler_read_key 4 ...@@ -246,8 +248,9 @@ Handler_read_key 4
Handler_read_rnd_next 5 Handler_read_rnd_next 5
# Status of testing query execution: # Status of testing query execution:
Variable_name Value Variable_name Value
Handler_read_key 4 Handler_read_key 5
Handler_read_rnd_next 5 Handler_read_rnd 3
Handler_read_rnd_next 12
Handler_update 3 Handler_update 3
DROP TABLE t1, t2; DROP TABLE t1, t2;
...@@ -263,13 +266,13 @@ INSERT INTO t2 VALUES (1), (2), (3); ...@@ -263,13 +266,13 @@ INSERT INTO t2 VALUES (1), (2), (3);
EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using where; FirstMatch(t1)
FLUSH STATUS; FLUSH STATUS;
FLUSH TABLES; FLUSH TABLES;
EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3); EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT b FROM t2 WHERE t1.a < 3);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 Using where; FirstMatch(t1)
Warnings: Warnings:
Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
# Status of EXPLAIN EXTENDED query # Status of EXPLAIN EXTENDED query
...@@ -900,14 +903,16 @@ INSERT INTO t2 VALUES (1), (2), (3), (1000); ...@@ -900,14 +903,16 @@ INSERT INTO t2 VALUES (1), (2), (3), (1000);
# #
EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2); EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where 1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3
FLUSH STATUS; FLUSH STATUS;
FLUSH TABLES; FLUSH TABLES;
EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2); EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT a FROM t2);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 4 100.00 Using where 1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00
# Status of EXPLAIN EXTENDED query # Status of EXPLAIN EXTENDED query
Variable_name Value Variable_name Value
Handler_read_key 4 Handler_read_key 4
...@@ -928,8 +933,8 @@ Handler_read_key 4 ...@@ -928,8 +933,8 @@ Handler_read_key 4
Handler_read_rnd_next 9 Handler_read_rnd_next 9
# Status of testing query execution: # Status of testing query execution:
Variable_name Value Variable_name Value
Handler_read_key 4 Handler_read_key 7
Handler_read_rnd_next 10 Handler_read_rnd_next 8
Handler_update 3 Handler_update 3
DROP TABLE t1, t2; DROP TABLE t1, t2;
...@@ -986,14 +991,14 @@ INSERT INTO t1 VALUES (1), (2), (3), (4), (5); ...@@ -986,14 +991,14 @@ INSERT INTO t1 VALUES (1), (2), (3), (4), (5);
# #
EXPLAIN DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2); EXPLAIN DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 5
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 5 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; FirstMatch(t1)
FLUSH STATUS; FLUSH STATUS;
FLUSH TABLES; FLUSH TABLES;
EXPLAIN EXTENDED DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2); EXPLAIN EXTENDED DELETE FROM t1 WHERE a1 IN (SELECT a2 FROM t2 WHERE a2 > 2);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 5 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 5 100.00 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 100.00 Using where; FirstMatch(t1)
# Status of EXPLAIN EXTENDED query # Status of EXPLAIN EXTENDED query
Variable_name Value Variable_name Value
Handler_read_key 4 Handler_read_key 4
...@@ -2823,14 +2828,14 @@ INSERT INTO t2 VALUES (1), (2), (3); ...@@ -2823,14 +2828,14 @@ INSERT INTO t2 VALUES (1), (2), (3);
EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); EXPLAIN UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
2 DEPENDENT SUBQUERY <derived3> unique_subquery distinct_key distinct_key 5 func 1 1 PRIMARY <derived3> ref key0 key0 5 test.t1.a 2 FirstMatch(t1)
3 DERIVED t2 ALL NULL NULL NULL NULL 3 Using filesort 3 DERIVED t2 ALL NULL NULL NULL NULL 3 Using filesort
FLUSH STATUS; FLUSH STATUS;
FLUSH TABLES; FLUSH TABLES;
EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x); EXPLAIN EXTENDED UPDATE t1 SET a = 10 WHERE a IN (SELECT * FROM (SELECT b FROM t2 ORDER BY b LIMIT 2,2) x);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where 1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
2 DEPENDENT SUBQUERY <derived3> unique_subquery distinct_key distinct_key 5 func 1 100.00 1 PRIMARY <derived3> ref key0 key0 5 test.t1.a 2 100.00 FirstMatch(t1)
3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort 3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using filesort
# Status of EXPLAIN EXTENDED query # Status of EXPLAIN EXTENDED query
Variable_name Value Variable_name Value
......
...@@ -4466,6 +4466,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { ...@@ -4466,6 +4466,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 {
} }
} }
}, },
{
"table": "t0",
"rowid_filters": [
{
"key": "a",
"build_cost": 0.174715752,
"rows": 3
}
]
},
{ {
"selectivity_for_indexes": [ "selectivity_for_indexes": [
{ {
...@@ -4531,6 +4541,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { ...@@ -4531,6 +4541,16 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 {
} }
} }
}, },
{
"table": "t1",
"rowid_filters": [
{
"key": "a",
"build_cost": 0.174715752,
"rows": 3
}
]
},
{ {
"selectivity_for_indexes": [ "selectivity_for_indexes": [
{ {
......
...@@ -20,9 +20,9 @@ delimiter ;| ...@@ -20,9 +20,9 @@ delimiter ;|
--change_user foo --change_user foo
set optimizer_trace="enabled=on"; set optimizer_trace="enabled=on";
# --error 1142 --error 1142
# select * from db1.t1; select * from db1.t1;
# select * from information_schema.OPTIMIZER_TRACE; select * from information_schema.OPTIMIZER_TRACE;
set optimizer_trace="enabled=off"; set optimizer_trace="enabled=off";
--change_user root --change_user root
......
This diff is collapsed.
This diff is collapsed.
...@@ -567,11 +567,11 @@ bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd) ...@@ -567,11 +567,11 @@ bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd)
switch (thd->lex->sql_command) { switch (thd->lex->sql_command) {
case SQLCOM_UPDATE: case SQLCOM_UPDATE:
return return
!((Sql_cmd_update *) cmd)->is_multitable() || !((Sql_cmd_update *) cmd)->is_multitable() &&
((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd); ((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd);
case SQLCOM_DELETE: case SQLCOM_DELETE:
return return
!((Sql_cmd_delete *) cmd)->is_multitable() || !((Sql_cmd_delete *) cmd)->is_multitable() &&
((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd); ((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd);
default: default:
return false; return false;
......
...@@ -491,8 +491,7 @@ void Opt_trace_start::init(THD *thd, ...@@ -491,8 +491,7 @@ void Opt_trace_start::init(THD *thd,
!list_has_optimizer_trace_table(tbl) && !list_has_optimizer_trace_table(tbl) &&
!sets_var_optimizer_trace(sql_command, set_vars) && !sets_var_optimizer_trace(sql_command, set_vars) &&
!thd->system_thread && !thd->system_thread &&
!ctx->disable_tracing_if_required() && !ctx->disable_tracing_if_required())
!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
{ {
ctx->start(thd, tbl, sql_command, query, query_length, query_charset, ctx->start(thd, tbl, sql_command, query, query_length, query_charset,
thd->variables.optimizer_trace_max_mem_size); thd->variables.optimizer_trace_max_mem_size);
......
...@@ -1214,7 +1214,7 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, ...@@ -1214,7 +1214,7 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
} }
} }
else if (thd->lex->sql_command == SQLCOM_DELETE) else if (thd->lex->sql_command == SQLCOM_DELETE)
{ {
Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd); Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd);
if (cmd->is_multitable() || derived->derived->outer_select()) if (cmd->is_multitable() || derived->derived->outer_select())
materialize= false; materialize= false;
......
...@@ -348,7 +348,6 @@ bool Sql_cmd_delete::delete_from_single_table(THD *thd) ...@@ -348,7 +348,6 @@ bool Sql_cmd_delete::delete_from_single_table(THD *thd)
query_plan.using_filesort= FALSE; query_plan.using_filesort= FALSE;
THD_STAGE_INFO(thd, stage_init_update); THD_STAGE_INFO(thd, stage_init_update);
create_explain_query(thd->lex, thd->mem_root);
const bool delete_history= table_list->vers_conditions.delete_history; const bool delete_history= table_list->vers_conditions.delete_history;
DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set())); DBUG_ASSERT(!(delete_history && table_list->period_conditions.is_set()));
...@@ -1668,6 +1667,10 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) ...@@ -1668,6 +1667,10 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
{ {
goto err; goto err;
} }
if (!multitable &&
select_lex->sj_subselects.elements)
multitable= true;
} }
if (multitable) if (multitable)
......
...@@ -5972,8 +5972,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, ...@@ -5972,8 +5972,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->needed_reg=select->needed_reg; s->needed_reg=select->needed_reg;
select->quick=0; select->quick=0;
impossible_range= records == 0 && s->table->reginfo.impossible_range; impossible_range= records == 0 && s->table->reginfo.impossible_range;
if (join->thd->lex->sql_command == SQLCOM_SELECT && if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
s->table->init_cost_info_for_usable_range_rowid_filters(join->thd); s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
} }
if (!impossible_range) if (!impossible_range)
...@@ -382,7 +382,6 @@ bool Sql_cmd_update::update_single_table(THD *thd) ...@@ -382,7 +382,6 @@ bool Sql_cmd_update::update_single_table(THD *thd)
DBUG_ENTER("Sql_cmd_update::update_single_table"); DBUG_ENTER("Sql_cmd_update::update_single_table");
THD_STAGE_INFO(thd, stage_init_update); THD_STAGE_INFO(thd, stage_init_update);
create_explain_query(thd->lex, thd->mem_root);
thd->table_map_for_update= 0; thd->table_map_for_update= 0;
...@@ -2479,6 +2478,8 @@ int multi_update::do_updates() ...@@ -2479,6 +2478,8 @@ int multi_update::do_updates()
table = cur_table->table; table = cur_table->table;
if (table == table_to_update) if (table == table_to_update)
continue; // Already updated continue; // Already updated
if (table->file->pushed_rowid_filter)
table->file->disable_pushed_rowid_filter();
org_updated= updated; org_updated= updated;
tmp_table= tmp_tables[cur_table->shared]; tmp_table= tmp_tables[cur_table->shared];
tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache
...@@ -2673,7 +2674,8 @@ int multi_update::do_updates() ...@@ -2673,7 +2674,8 @@ int multi_update::do_updates()
check_opt_it.rewind(); check_opt_it.rewind();
while (TABLE *tbl= check_opt_it++) while (TABLE *tbl= check_opt_it++)
tbl->file->ha_rnd_end(); tbl->file->ha_rnd_end();
if (table->file->save_pushed_rowid_filter)
table->file->enable_pushed_rowid_filter();
} }
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -2684,6 +2686,8 @@ int multi_update::do_updates() ...@@ -2684,6 +2686,8 @@ int multi_update::do_updates()
} }
err2: err2:
if (table->file->save_pushed_rowid_filter)
table->file->enable_pushed_rowid_filter();
if (table->file->inited) if (table->file->inited)
(void) table->file->ha_rnd_end(); (void) table->file->ha_rnd_end();
if (tmp_table->file->inited) if (tmp_table->file->inited)
...@@ -2987,7 +2991,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd) ...@@ -2987,7 +2991,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
{ {
goto err; goto err;
} }
if (!multitable &&
select_lex->sj_subselects.elements)
multitable= true;
} }
if (table_list->has_period()) if (table_list->has_period())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment