mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
BUG#12915: Added single-table UPDATE/DELTE ... ORDER BY ... LIMIT
optimization: now can use index to find records to update/delete when there is no WHERE clause.
This commit is contained in:
@ -582,6 +582,94 @@ SEL_ARG *SEL_ARG::clone_tree()
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Find an index that allows to retrieve first #limit records in the given
|
||||
order cheaper then one would retrieve them using full table scan.
|
||||
|
||||
SYNOPSIS
|
||||
get_index_for_order()
|
||||
table Table to be accessed
|
||||
order Required ordering
|
||||
limit Number of records that will be retrieved
|
||||
|
||||
DESCRIPTION
|
||||
Run through all table indexes and find the shortest index that allows
|
||||
records to be retrieved in given order. If there is such index and
|
||||
reading first #limit records from it is cheaper then scanning the entire
|
||||
table, return it.
|
||||
|
||||
This function is used only by UPDATE/DELETE, so we take into account how
|
||||
the UPDATE/DELETE code will work:
|
||||
* index can only be scanned in forward direction
|
||||
* HA_EXTRA_KEYREAD will not be used
|
||||
Perhaps these assumptions could be relaxed
|
||||
|
||||
RETURN
|
||||
index number
|
||||
MAX_KEY if no such index was found.
|
||||
*/
|
||||
|
||||
uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit)
|
||||
{
|
||||
uint idx;
|
||||
uint match_key= MAX_KEY, match_key_len= MAX_KEY_LENGTH + 1;
|
||||
ORDER *ord;
|
||||
|
||||
for (ord= order; ord; ord= ord->next)
|
||||
if (!ord->asc)
|
||||
return MAX_KEY;
|
||||
|
||||
for (idx= 0; idx < table->keys; idx++)
|
||||
{
|
||||
if (!(table->keys_in_use_for_query.is_set(idx)))
|
||||
continue;
|
||||
KEY_PART_INFO *keyinfo= table->key_info[idx].key_part;
|
||||
uint partno= 0;
|
||||
|
||||
/*
|
||||
The below check is sufficient considering we now have either BTREE
|
||||
indexes (records are returned in order for any index prefix) or HASH
|
||||
indexes (records are not returned in order for any index prefix).
|
||||
*/
|
||||
if (!(table->file->index_flags(idx, 0, 1) & HA_READ_ORDER))
|
||||
continue;
|
||||
for (ord= order; ord; ord= ord->next, partno++)
|
||||
{
|
||||
Item *item= order->item[0];
|
||||
if (!(item->type() == Item::FIELD_ITEM &&
|
||||
((Item_field*)item)->field->eq(keyinfo[partno].field)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ord && table->key_info[idx].key_length < match_key_len)
|
||||
{
|
||||
/*
|
||||
Ok, the ordering is compatible and this key is shorter then
|
||||
previous match (we want shorter keys as we'll have to read fewer
|
||||
index pages for the same number of records)
|
||||
*/
|
||||
match_key= idx;
|
||||
match_key_len= table->key_info[idx].key_length;
|
||||
}
|
||||
}
|
||||
|
||||
if (match_key != MAX_KEY)
|
||||
{
|
||||
/*
|
||||
Found an index that allows records to be retrieved in the requested
|
||||
order. Now we'll check if using the index is cheaper then doing a table
|
||||
scan.
|
||||
*/
|
||||
double full_scan_time= table->file->scan_time();
|
||||
double index_scan_time= table->file->read_time(match_key, 1, limit);
|
||||
if (index_scan_time > full_scan_time)
|
||||
match_key= MAX_KEY;
|
||||
}
|
||||
return match_key;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Test if a key can be used in different ranges
|
||||
|
||||
|
Reference in New Issue
Block a user