1
0
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:
timour@mysql.com
2004-08-27 16:37:13 +03:00
parent a5f063e0da
commit e2cd3dd1ce
19 changed files with 5077 additions and 204 deletions

View File

@ -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)