How to optimize MySQL queries with constants?
NOTE: original question is controversial but crawls to the bottom for something meaningful.
I have a query that I want to optimize that looks something like this:
select cols from tbl where col = "some run time value" limit 1;
I want to know what keys are used, but all I pass to explain is it can optimize the where nothing clause ("Impossible WHERE noticed ...") because I gave it a constant.
- Is there a way to tell mysql not to do persistent optimizations in the explanation?
- Did I miss something?
- Is there a better way to get the information I need?
Edit: It EXPLAIN
seems to give me a query plan that will be derived from constant values. Since the query is part of a stored procedure (and IIRC query plans in the files are created before they are called) this makes me bad because the value is not constant. I want to know what query plan the optimizer will generate if it doesn't know what the actual value will be.
Am I missing something?
Edit2: Asking the question elsewhere, it looks like MySQL always rebuilds query plans unless you go out of your way to reuse them. Even in stored procedures. From this it seems that my question is moot.
However, it's not doing what I really wanted to find controversial: . How do you optimize a query that contains values that are constant in any particular query, but where I, the programmer, don't know in advance which value will be used? - For example, my client side code generates a request with a number in it where
. Several times this number leads to impossibility, when in other cases it will not. How can I use an explanation to check how optimized a query is?
The best approach I see from the start is to run EXPLAIN
on it for a complete matrix of existing / non-existing cases. In fact, this is not a very good solution, as it would be difficult and difficult to do it manually.
source to share
For example, my client side code generates a request with a number in it where the offer is.
Several times the number will lead to the impossible, if elsewhere it will not.
How can I use an explanation to check how optimized a query is?
MySQL
builds different query plans for different values of related parameters.
In this article, you can read a list of when the optimizer MySQL
does what:
Action When Query parse PREPARE Negation elimination PREPARE Subquery re-writes PREPARE Nested JOIN simplification First EXECUTE OUTER-> INNER JOIN conversions First EXECUTE Partition pruning Every EXECUTE COUNT / MIN / MAX elimination Every EXECUTE Constant subexpression removal Every EXECUTE Equality propagation Every EXECUTE Constant table detection Every EXECUTE ref access analysis Every EXECUTE range / index_merge analysis and optimization Every EXECUTE Join optimization Every EXECUTE
One more thing is missing from this list.
MySQL
can rebuild the query plan for each iteration JOIN
: a such as range checking for each record
.
If you have a pivot index on a table:
CREATE INDEX ix_table2_col1_col2 ON table2 (col1, col2)
and a request like this:
SELECT *
FROM table1 t1
JOIN table2 t2
ON t2.col1 = t1.value1
AND t2.col2 BETWEEN t1.value2_lowerbound AND t2.value2_upperbound
MySQL
Will NOT use the index RANGE
from (t1.value1, t1.value2_lowerbound)
before (t1.value1, t1.value2_upperbound)
. Instead, it will use the access to the index REF
to (t1.value)
simply filter out invalid values.
But if you rewrite your query like this:
SELECT *
FROM table1 t1
JOIN table2 t2
ON t2.col1 <= t1.value1
AND t2.col1 >= t2.value1
AND t2.col2 BETWEEN t1.value2_lowerbound AND t2.value2_upperbound
then it MySQL
will check the index RANGE
for each record from table1
and decide whether to use access RANGE
on the fly.
You can read about it in these articles on your blog:
- Choosing timestamps for a timezone - how to use coarse filtering to filter timestamps without a timezone
- SKIP SCAN emulation - how to emulate an access method
SKIP SCAN
inMySQL
- Analytic functions: optimization LAG, LEAD, FIRST_VALUE, LAST_VALUE - how to emulate Oracle analytic functions in
MySQL
- Advanced row fetching - how to select
N
records from each group inMySQL
All this uses range checking for each record
Coming back to your question: it is impossible to determine which plan to use MySQL
for each given constant, since there is no plan before the constant.
Unfortunately, there is no way to force MySQL
a single query plan to be used for each associated parameter value.
You can control the order of JOIN
and INDEX
'selected with STRAIGHT_JOIN
and clauses FORCE INDEX
, but they will not force a specific index path or deny IMPOSSIBLE WHERE
.
On the other hand, everyone JOIN
's MySQL
only used NESTED LOOPS
. This means that if you create the correct JOIN
ordering or select the desired index, it MySQL
will probably benefit from everyone IMPOSSIBLE WHERE
.
source to share
How do you optimize a query with values that are constant for the query only, but where I, the programmer, don't know in advance what value will be used?
Using indexes on specific columns (or even on a combination of columns if you always query the given columns together). If you have indexes, the query planner will potentially use them.
As for the "impossible" values, the query planner might conclude that the given value is missing from the table from multiple sources:
- if there is a pointer to a specific column, it can notice that the specific value is large or less than any value in the index (min / max values take constant time to retrieve from indices)
- if you are using the wrong type (if you are asking for a numeric column to be equal to text)
PS. In general, creating a query plan is not expensive and is better to recreate than reuse, since conditions can change since the query plan is created and a better query plan can arise.
source to share