Search code examples
mysqlsqlsubqueryquery-tuning

Optimize very slow Mysql Query


I need help optimizing this query:

SELECT 
    c.rut, c.nombre, c.apellido, c.estado, c.porcentajeavance,  
    c.porcentajenota, c.nota, c.curso, c.fecha_inicio,   
    c.fecha_termino, c.grupo, c.fecha, c.cargo
FROM tbl_historico c
WHERE fecha = ( 
    SELECT max( t.fecha ) fecha
    FROM tbl_historico t
    WHERE t.rut = c.rut AND c.curso = t.curso 
)

EXPLAIN output:

+----+--------------------+-------+------+-----------------+-------+---------+-----------------------------------------+--------+-------------+
| id | select_type        | table | type | possible_keys   | key   | key_len | ref                                     | rows   | Extra       |
+----+--------------------+-------+------+-----------------+-------+---------+-----------------------------------------+--------+-------------+
|  1 | PRIMARY            | c     | ALL  | NULL            | NULL  | NULL    | NULL                                    | 158008 | Using where |
|  2 | DEPENDENT SUBQUERY | t     | ref  | rut,rut_2,rut_3 | rut_3 | 514     | campus_mdle1.c.rut,campus_mdle1.c.curso |     27 | Using index |
+----+--------------------+-------+------+-----------------+-------+---------+-----------------------------------------+--------+-------------+

Solution

  • I think you can rewrite it to avoid the correlated subquery:

    SELECT c.rut, c.nombre, c.apellido, c.estado, c.porcentajeavance
         , c.porcentajenota, c.nota, c.curso, c.fecha_inicio
         , c.fecha_termino, c.grupo, c.fecha, c.cargo
    FROM 
          tbl_historico AS c
      JOIN
          ( SELECT rut, curso, MAX(fetcha) AS fetcha
            FROM tbl_historico 
            GROUP BY rut, curso 
          ) AS grp
        ON (grp.rut, grp.curso, grp.fetcha)
         = ( c.rut,   c.curso,   c.fetcha)
    

    An index on (rut, curso, fetcha) would be good for this query.


    Another solution would be:

    SELECT c.rut, c.nombre, c.apellido, c.estado, c.porcentajeavance
         , c.porcentajenota, c.nota, c.curso, c.fecha_inicio
         , c.fecha_termino, c.grupo, c.fecha, c.cargo
    FROM 
          ( SELECT rut, curso
            FROM tbl_historico
            GROUP BY rut, curso
            ORDER BY rut, curso                --- custom order and
            LIMIT 30 OFFSET 0                  --- limit here
          ) AS dc
      JOIN
          tbl_historico AS c
        ON c.PK =                              --- the Primary Key of the table here
           ( SELECT h.PK                       --- and here
             FROM tbl_historico AS h
             WHERE (h.rut, h.curso) = (dc.rut, dc.curso)
             ORDER BY h.fetcha DESC
             LIMIT 1  
           ) 
    

    This will show different result (in case of ties, only one of the tied rows will be shown) but it could be even faster if you want to limit the number of rows.