The problem was histograms, I ran statistics and disabled histogram creation and the execution plan used nested loops:
BEGIN
DBMS_STATS.GATHER_table_STATS (OWNNAME => 'MIDAS', TABNAME => 'MINCISOC',
METHOD_OPT => 'FOR ALL COLUMNS SIZE 1');
END;
If I run it with FOR ALL COLUMNS SIZE AUTO
again the same problem because it uses hash join. Thanks to Phil for the suggestion.
Multiplying the rows is invalid for several reasons:
- Many times, the rows examined are an approximation (based on statistics, not accurate), good for query plan selection, but not for performance calculation
- The total number of rows examined on a nested loop join
(A, B)
is not rows_examined_on_table_A * rows_examined_on_table_B
, but rows_examined_on_table_A + rows_returned_from_table_A * rows_examined_on_table_B
. Where clauses can make a huge difference on that, although it is true that the mentioned calculations is many times used as a broad approximation, assuming the indexes are being created properly and the main causes of filtering out results.
- Modern MySQL versions do not use always a nested loop join approach for executing joins and subqueries. Check 5.6 subquery optimizations and other optimization documents on the same manual. Additionally, some of the new optimization techniques do not modify the predicted examined rows, which at some times can be way lower than the one printed, even if it has been calculated exactly.
In particular, on your first query, you are hitting a well know MySQL bug? limitation? in which an IN subquery is identified as a DEPENDENT SUBQUERY, even if it really isn't, forcing the outmost query to be executed without an index (full table scan) in order to test all possible values of the first table. That is usually an indicator that it is a bad query. It seems not to bee too bad in this case, as the table is small, but it is usually an indication of bad performance.
The other thing that should bring your attention is the Using temporary; Using filesort
. Filtering is not the only thing where you should focus, as these extra pieces of information are telling you that a large sorting has to be done using a temporary table (that may or may not end up on disk, but at least has to be materialized). That is another indicator of potential bad performance, that in some cases can be avoided with the right indexes.
I will not tell you which is the right query to use (partially, because I do not know all the variables: indexes, tables structure, etc., and in most cases it will depend on the particular hardware/resources available), but I will tell you the tools to decide:
Profile the query- obtain the post execution times and how much of it it is being invested in what. You can use SHOW PROFILES up to 5.5, and the performance_schema starting with 5.6.
As time can be sometimes variable (for example, depending on other queries being executed at the same time, depending on the buffer pool contents) Obtain post-execution statistics with SHOW SESSION STATUS
. In particular:
FLUSH SESSION STATUS;
SELECT ... ;
SHOW STATUS like 'Hand%';
will give you the exact number of handler calls done (approximately, the number of rows read and written for that particular query- although that is not 100% accurate, as it depends on the particular engine implementation).
You may also want to monitor other status variables, like the created temporary tables, created temporary tables on disk and sort passes/sorted rows.
All of these will give you post-execution, exact, time-independent parameters to evaluate the performance of a query. Percona even has a patch for the slow log to output that information on the logs instead of using performance_schema.
With those extra pieces of information you will be able to evaluate more objectively which query is better, and not relying exclusively on EXPLAIN, which only provides limited pre-execution information.
Best Answer
There are al lot of passibilities to force mariadb
for example:
how they work and how to use it
https://mariadb.com/kb/en/index-hints-how-to-force-query-plans/