Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL is not using INDEX in subquery

I have these tables and queries as defined in sqlfiddle.

First my problem was to group people showing LEFT JOINed visits rows with the newest year. That I solved using subquery.

Now my problem is that that subquery is not using INDEX defined on visits table. That is causing my query to run nearly indefinitely on tables with approx 15000 rows each.

Here's the query. The goal is to list every person once with his newest (by year) record in visits table.

Unfortunately on large tables it gets real sloooow because it's not using INDEX in subquery.

SELECT *
FROM people
LEFT JOIN (
  SELECT *
  FROM visits
  ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id

Does anyone know how to force MySQL to use INDEX already defined on visits table?

like image 738
meridius Avatar asked Sep 03 '12 13:09

meridius


People also ask

How to index a sub-query in MySQL?

Yet it doesn't mean it's impossible to come across a scenario in which you wish you could index a sub-query. In which cases you've got two choices, first is using a temporary table as you pointed out yourself, holding the results of the sub-query. This solution is advantageous since it is supported by MySQL for a long time.

What version of MySQL do you use for indexing?

The second solution is using MySQL version 5.6 or above. In recent versions of MySQL new algorithms are incorporated so an index defined on a table used within a sub-query can also be used outside of the sub-query. For the edited version of the question I would recommend the following solution:

Can a subquery be nested in MySQL?

Also, a subquery can be nested within another subquery. A MySQL subquery is called an inner query while the query that contains the subquery is called an outer query. A subquery can be used anywhere that expression is used and must be closed in parentheses.

When executing the query MySQL evaluates the subquery first?

When executing the query, MySQL evaluates the subquery first and uses the result of the subquery for the outer query. We will use the table payments in the sample database for the demonstration.


1 Answers

Your query:

SELECT *
FROM people
LEFT JOIN (
  SELECT *
  FROM visits
  ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id;
  • First, is using non-standard SQL syntax (items appear in the SELECT list that are not part of the GROUP BY clause, are not aggregate functions and do not sepend on the grouping items). This can give indeterminate (semi-random) results.

  • Second, ( to avoid the indeterminate results) you have added an ORDER BY inside a subquery which (non-standard or not) is not documented anywhere in MySQL documentation that it should work as expected. So, it may be working now but it may not work in the not so distant future, when you upgrade to MySQL version X (where the optimizer will be clever enough to understand that ORDER BY inside a derived table is redundant and can be eliminated).

Try using this query:

SELECT 
    p.*, v.*
FROM 
    people AS p
  LEFT JOIN 
        ( SELECT 
              id_people
            , MAX(year) AS year
          FROM
              visits
          GROUP BY
              id_people
         ) AS vm
      JOIN
          visits AS v
        ON  v.id_people = vm.id_people
        AND v.year = vm.year 
    ON  v.id_people = p.id;

The: SQL-fiddle

A compound index on (id_people, year) would help efficiency.


A different approach. It works fine if you limit the persons to a sensible limit (say 30) first and then join to the visits table:

SELECT 
    p.*, v.*
FROM 
    ( SELECT *
      FROM people
      ORDER BY name
        LIMIT 30
    ) AS p
  LEFT JOIN 
    visits AS v
      ON  v.id_people = p.id
      AND v.year =
    ( SELECT 
          year
      FROM
          visits
      WHERE
          id_people = p.id
      ORDER BY
          year DESC
        LIMIT 1
     )  
ORDER BY name ;
like image 127
ypercubeᵀᴹ Avatar answered Oct 11 '22 09:10

ypercubeᵀᴹ