mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
WL#1724 "Min/Max Optimization for Queries with Group By Clause"
- after-review changes - merged with the source tree from 204-08-27
This commit is contained in:
@ -188,6 +188,7 @@ static bool update_sum_func(Item_sum **func);
|
||||
static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
|
||||
bool distinct, const char *message=NullS);
|
||||
static Item *remove_additional_cond(Item* conds);
|
||||
static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
|
||||
|
||||
|
||||
/*
|
||||
@ -635,7 +636,7 @@ JOIN::optimize()
|
||||
conds=new Item_int((longlong) 1,1); // Always true
|
||||
}
|
||||
select=make_select(*table, const_table_map,
|
||||
const_table_map, conds, &error);
|
||||
const_table_map, conds, &error, true);
|
||||
if (error)
|
||||
{ /* purecov: inspected */
|
||||
error= -1; /* purecov: inspected */
|
||||
@ -2304,6 +2305,12 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
|
||||
if (s->worst_seeks < 2.0) // Fix for small tables
|
||||
s->worst_seeks=2.0;
|
||||
|
||||
/*
|
||||
Add to stat->const_keys those indexes for which all group fields or
|
||||
all select distinct fields participate in one index.
|
||||
*/
|
||||
add_group_and_distinct_keys(join, s);
|
||||
|
||||
if (!s->const_keys.is_clear_all() &&
|
||||
!s->table->pos_in_table_list->embedding)
|
||||
{
|
||||
@ -2312,7 +2319,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
|
||||
select= make_select(s->table, found_const_table_map,
|
||||
found_const_table_map,
|
||||
s->on_expr ? s->on_expr : conds,
|
||||
&error);
|
||||
&error, true);
|
||||
if (!select)
|
||||
DBUG_RETURN(1);
|
||||
records= get_quick_record_count(join->thd, select, s->table,
|
||||
&s->const_keys, join->row_limit);
|
||||
s->quick=select->quick;
|
||||
@ -2347,13 +2356,13 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
|
||||
}
|
||||
}
|
||||
|
||||
/* Find best combination and return it */
|
||||
join->join_tab=stat;
|
||||
join->map2table=stat_ref;
|
||||
join->table= join->all_tables=table_vector;
|
||||
join->const_tables=const_count;
|
||||
join->found_const_table_map=found_const_table_map;
|
||||
|
||||
/* Find an optimal join order of the non-constant tables. */
|
||||
if (join->const_tables != join->tables)
|
||||
{
|
||||
optimize_keyuse(join, keyuse_array);
|
||||
@ -2365,6 +2374,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
|
||||
sizeof(POSITION)*join->const_tables);
|
||||
join->best_read=1.0;
|
||||
}
|
||||
/* Generate an execution plan from the found optimal join order. */
|
||||
DBUG_RETURN(join->thd->killed || get_best_combination(join));
|
||||
}
|
||||
|
||||
@ -2561,6 +2571,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond,
|
||||
|
||||
bool is_const=1;
|
||||
for (uint i=0; i<num_values; i++)
|
||||
/*
|
||||
TODO: this looks like a bug, should be
|
||||
is_const&= (value[i])->const_item();
|
||||
*/
|
||||
is_const&= (*value)->const_item();
|
||||
if (is_const)
|
||||
stat[0].const_keys.merge(possible_keys);
|
||||
@ -2997,6 +3011,68 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Discover the indexes that can be used for GROUP BY or DISTINCT queries.
|
||||
|
||||
SYNOPSIS
|
||||
add_group_and_distinct_keys()
|
||||
join
|
||||
join_tab
|
||||
|
||||
DESCRIPTION
|
||||
If the query has a GROUP BY clause, find all indexes that contain all
|
||||
GROUP BY fields, and add those indexes to join->const_keys.
|
||||
If the query has a DISTINCT clause, find all indexes that contain all
|
||||
SELECT fields, and add those indexes to join->const_keys.
|
||||
This allows later on such queries to be processed by a
|
||||
QUICK_GROUP_MIN_MAX_SELECT.
|
||||
|
||||
RETURN
|
||||
None
|
||||
*/
|
||||
|
||||
static void
|
||||
add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
|
||||
{
|
||||
List<Item_field> indexed_fields;
|
||||
List_iterator<Item_field> indexed_fields_it(indexed_fields);
|
||||
ORDER *cur_group;
|
||||
Item_field *cur_item;
|
||||
key_map possible_keys(0);
|
||||
|
||||
if (join->group_list)
|
||||
{ /* Collect all query fields referenced in the GROUP clause. */
|
||||
for (cur_group= join->group_list; cur_group; cur_group= cur_group->next)
|
||||
(*cur_group->item)->walk(&Item::collect_item_field_processor,
|
||||
(byte*) &indexed_fields);
|
||||
}
|
||||
else if (join->select_distinct)
|
||||
{ /* Collect all query fields referenced in the SELECT clause. */
|
||||
List<Item> &select_items= join->fields_list;
|
||||
List_iterator<Item> select_items_it(select_items);
|
||||
Item *item;
|
||||
while ((item= select_items_it++))
|
||||
item->walk(&Item::collect_item_field_processor, (byte*) &indexed_fields);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (indexed_fields.elements == 0)
|
||||
return;
|
||||
|
||||
/* Intersect the keys of all group fields. */
|
||||
cur_item= indexed_fields_it++;
|
||||
possible_keys.merge(cur_item->field->part_of_key);
|
||||
while ((cur_item= indexed_fields_it++))
|
||||
{
|
||||
possible_keys.intersect(cur_item->field->part_of_key);
|
||||
}
|
||||
|
||||
if (!possible_keys.is_clear_all())
|
||||
join_tab->const_keys.merge(possible_keys);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Go through all combinations of not marked tables and find the one
|
||||
which uses least records
|
||||
@ -4883,20 +4959,23 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
if (select)
|
||||
{
|
||||
table_map used_tables;
|
||||
if (join->tables > 1)
|
||||
cond->update_used_tables(); // Tablenr may have changed
|
||||
if (join->const_tables == join->tables &&
|
||||
join->thd->lex->current_select->master_unit() ==
|
||||
&join->thd->lex->unit) // not upper level SELECT
|
||||
join->const_table_map|=RAND_TABLE_BIT;
|
||||
{ // Check const tables
|
||||
COND *const_cond=
|
||||
make_cond_for_table(cond,join->const_table_map,(table_map) 0);
|
||||
DBUG_EXECUTE("where",print_where(const_cond,"constants"););
|
||||
if (const_cond && !const_cond->val_int())
|
||||
{
|
||||
DBUG_PRINT("info",("Found impossible WHERE condition"));
|
||||
DBUG_RETURN(1); // Impossible const condition
|
||||
if (cond) /* Because of QUICK_GROUP_MIN_MAX_SELECT */
|
||||
{ /* there may be a select without a cond. */
|
||||
if (join->tables > 1)
|
||||
cond->update_used_tables(); // Tablenr may have changed
|
||||
if (join->const_tables == join->tables &&
|
||||
join->thd->lex->current_select->master_unit() ==
|
||||
&join->thd->lex->unit) // not upper level SELECT
|
||||
join->const_table_map|=RAND_TABLE_BIT;
|
||||
{ // Check const tables
|
||||
COND *const_cond=
|
||||
make_cond_for_table(cond,join->const_table_map,(table_map) 0);
|
||||
DBUG_EXECUTE("where",print_where(const_cond,"constants"););
|
||||
if (const_cond && !const_cond->val_int())
|
||||
{
|
||||
DBUG_PRINT("info",("Found impossible WHERE condition"));
|
||||
DBUG_RETURN(1); // Impossible const condition
|
||||
}
|
||||
}
|
||||
}
|
||||
used_tables=((select->const_tables=join->const_table_map) |
|
||||
@ -4928,8 +5007,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
join->best_positions[i].records_read= rows2double(tab->quick->records);
|
||||
}
|
||||
|
||||
COND *tmp=make_cond_for_table(cond,used_tables,current_map);
|
||||
if (!tmp && tab->quick)
|
||||
COND *tmp= NULL;
|
||||
if (cond)
|
||||
tmp= make_cond_for_table(cond,used_tables,current_map);
|
||||
if (cond && !tmp && tab->quick)
|
||||
{ // Outer join
|
||||
/*
|
||||
Hack to handle the case where we only refer to a table
|
||||
@ -4937,9 +5018,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
*/
|
||||
tmp=new Item_int((longlong) 1,1); // Always true
|
||||
}
|
||||
if (tmp)
|
||||
if (tmp || !cond)
|
||||
{
|
||||
DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name););
|
||||
if (tmp)
|
||||
DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name););
|
||||
SQL_SELECT *sel=tab->select=(SQL_SELECT*)
|
||||
join->thd->memdup((gptr) select, sizeof(SQL_SELECT));
|
||||
if (!sel)
|
||||
@ -4949,10 +5031,18 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
add a match guard to the pushed down predicate.
|
||||
The guard will turn the predicate on only after
|
||||
the first match for outer tables is encountered.
|
||||
*/
|
||||
if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0)))
|
||||
DBUG_RETURN(1);
|
||||
tab->select_cond=sel->cond=tmp;
|
||||
*/
|
||||
if (cond)
|
||||
{/*
|
||||
Because of QUICK_GROUP_MIN_MAX_SELECT there may be a select without
|
||||
a cond, so neutralize the hack above.
|
||||
*/
|
||||
if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0)))
|
||||
DBUG_RETURN(1);
|
||||
tab->select_cond=sel->cond=tmp;
|
||||
}
|
||||
else
|
||||
tab->select_cond= sel->cond= NULL;
|
||||
|
||||
sel->head=tab->table;
|
||||
if (tab->quick)
|
||||
@ -4992,7 +5082,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
the index if we are using limit and this is the first table
|
||||
*/
|
||||
|
||||
if ((!tab->keys.is_subset(tab->const_keys) && i > 0) ||
|
||||
if (cond &&
|
||||
(!tab->keys.is_subset(tab->const_keys) && i > 0) ||
|
||||
(!tab->const_keys.is_clear_all() && i == join->const_tables &&
|
||||
join->unit->select_limit_cnt <
|
||||
join->best_positions[i].records_read &&
|
||||
@ -5050,7 +5141,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
}
|
||||
if (i != join->const_tables && tab->use_quick != 2)
|
||||
{ /* Read with cache */
|
||||
if ((tmp=make_cond_for_table(cond,
|
||||
if (cond &&
|
||||
(tmp=make_cond_for_table(cond,
|
||||
join->const_table_map |
|
||||
current_map,
|
||||
current_map)))
|
||||
@ -7444,11 +7536,18 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (join->sort_and_group || (join->procedure &&
|
||||
join->procedure->flags & PROC_GROUP))
|
||||
end_select=end_send_group;
|
||||
/* Test if data is accessed via QUICK_GROUP_MIN_MAX_SELECT. */
|
||||
bool is_using_quick_group_min_max_select=
|
||||
(join->join_tab->select && join->join_tab->select->quick &&
|
||||
(join->join_tab->select->quick->get_type() ==
|
||||
QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX));
|
||||
|
||||
if ((join->sort_and_group ||
|
||||
(join->procedure && join->procedure->flags & PROC_GROUP)) &&
|
||||
!is_using_quick_group_min_max_select)
|
||||
end_select= end_send_group;
|
||||
else
|
||||
end_select=end_send;
|
||||
end_select= end_send;
|
||||
}
|
||||
join->join_tab[join->tables-1].next_select=end_select;
|
||||
|
||||
@ -9225,7 +9324,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
& HA_READ_PREV) ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
DBUG_RETURN(0); // Use filesort
|
||||
|
||||
/* ORDER BY range_key DESC */
|
||||
@ -11455,11 +11555,16 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
||||
extra.append(tab->keys.print(buf));
|
||||
extra.append(')');
|
||||
}
|
||||
else
|
||||
else if (tab->select->cond)
|
||||
extra.append("; Using where");
|
||||
}
|
||||
if (key_read)
|
||||
extra.append("; Using index");
|
||||
{
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
extra.append("; Using index for group-by");
|
||||
else
|
||||
extra.append("; Using index");
|
||||
}
|
||||
if (table->reginfo.not_exists_optimize)
|
||||
extra.append("; Not exists");
|
||||
if (need_tmp_table)
|
||||
|
Reference in New Issue
Block a user