MySQL: Índice cuando se unen a las tablas no se utilizan (Optimización del rendimiento de interrogación)

dba.stackexchange https://dba.stackexchange.com/questions/872

Pregunta

necesito para optimizar la siguiente consulta:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

EXPLAIN SELECT me da el siguiente resultado:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

¿Por qué se saca primero los artículos y luego las entradas del blog? ¿Es porque tienen más entradas entradas del blog? Y ¿cómo puedo mejorar la consulta para que el articlepost puede utilizar un índice?

Actualización: Un índice se encuentra en blogposts.date_created. Extracción de la enfermedad como blogposts.title y la date_published <= NOW () no hace nada.

Cuando quito el " articles.id AS articleid " se puede utilizar el Índice blogpost_id en los artículos ... Suena extraño para mí, alguien sabe por qué? (Porque en realidad lo necesito ..)

La nueva explicar es similar al siguiente:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  articles    index blogpost_id blogpost_id    4    NULL    6915    Using index; Using temporary; Using filesort
1   SIMPLE  blogposts   eq_ref  PRIMARY PRIMARY 4   articles.blogpost_id    1   Using where
¿Fue útil?

Solución

Me tomó un vistazo más de cerca a la consulta y que podría ser capaz de rediseñar. Esto es lo que quiero decir:

La parte LIMIT 0,50 de la consulta parece estar hecho ocupada en la última consulta.

Se puede mejorar el diseño de la consulta haciendo lo siguiente:

Paso 1) Crear una consulta en línea para recoger solamente las teclas. En este caso, el identificador de entradas del blog.

Paso 2) Imponer cualquier lugar, ORDER BY y GROUP BY cláusulas sobre la consulta en línea con lo que las llaves.

Paso 3) imposta la cláusula LIMIT como el último paso de hacer la consulta en línea.

Paso 4) Únete a la consulta en línea con la mesa de entrada de blog en el caso de que necesite más columnas de entrada de blog como un blostpost TempTable

Paso 5) Formar parte de este nuevo TempTable entrada de blog con la tabla de artículos.

Pasos 1-3 está destinado a crear un TempTable con exactamente 50 filas y incluir el ID blogpost. A continuación, lleve a cabo todas las combinaciones último lugar.

Con estas medidas aplicadas a su búsqueda original, usted debe tener este aspecto:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    AND title LIKE '%{de}%'
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

Desde que ha editado la pregunta y declaró que va a borrar LIKE, ahora su consulta debe mirar más a esto:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT B.*
  FROM
  (
    SELECT id FROM blogposts
    WHERE date_published <= NOW()
    AND deleted = 0 AND visible = 1
    ORDER BY date_created DESC
    LIMIT 0,50
  ) A
  INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

En la frase [cosas omitidas], si no necesita nada de entradas del blog que no sean las teclas, a continuación, la consulta debería tener este aspecto:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
  SELECT id FROM blogposts
  WHERE date_published <= NOW()
  AND deleted = 0 AND visible = 1
  ORDER BY date_created DESC
  LIMIT 0,50
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;

CAVEAT

Asegúrese de que usted construye un índice que involucra las columnas borradas, visible y DATE_CREATED de la siguiente manera:

ALTER TABLE blogposts ADD INDEX deleted_visible_date_created (deleted,visible,date_created);

darle una oportunidad !!!

Otros consejos

Su condición donde blogposts.title LIKE '%{de}%' causará un escaneo completo de tabla en la página tabla entradas del blog. Es probable que exploran figuras MySQL 6915 artículos es más eficiente.

En cuanto a la forma de mejorarlo, es posible añadir un índice en entradas del blog utilizando el date_created o date_published y añadir un rango de la a la condición WHERE (algo que no sea <= NOW ())

Y COMO blogposts.title '%} {% de' - Inútil para la optimización (que conduce comodín)

donde blogposts.deleted = 0 Y blogposts.visible = 1 - Yuck:. Si no se deben mostrar, sacarlos de la tabla

Y blogposts.date_published <= NOW () ORDER BY DESC blogposts.date_created LIMIT 0, 50 - que conduce a la necesidad de índice (date_published) (Sin embargo, similares y banderas pueden impedir que este índice sea utilizado.)

Por favor proporcione SHOW CREATE TABLE ...; SHOW TABLE STATUS ...;

Muchas gracias:)

Me lo dio una oportunidad y descubrió que en realidad su último comentario sobre el índice era lo que necesitaba. A veces es sólo una pequeña mejora que se necesita ...;) Comparación:

Antiguo consulta:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50

Nueva consulta:

SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
    SELECT B.*
    FROM
    (
        SELECT id FROM blogposts
        WHERE date_published <= NOW()
        AND deleted = 0 AND visible = 1
        AND title LIKE '%{de}%'
        ORDER BY date_created DESC
        LIMIT 0,50
    ) A
    INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id

Ahora, sin mencionar el índice:

Antiguo Explicar Resultado:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where

Nueva Explicar Resultado:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ALL     deleted,visible,date_published  deleted     1       28198   Using filesort

Explicar resultado con índice en la consulta de edad:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  blogposts   ref     PRIMARY,deleted,visible,date_published,deleted_vis...   deleted_visible_date_created    2   const,const     27771   Using where
1   SIMPLE  articles    ref     blogposts_id    blogposts_id    4   db.blogposts.id     1

Explicar resultado con índice en nueva consulta:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    50   
1   PRIMARY     articles    ref     blogposts_id    blogposts_id    4   blogposts.id    1    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    50   
2   DERIVED     B   eq_ref  PRIMARY     PRIMARY     4   A.id    1    
3   DERIVED     blogposts   ref     deleted,visible,date_published,deleted_visible_dat...   deleted_visible_date_created    2       27771   Using where

Velocidad en la consulta de edad sin / con índice: 0,1835 / 0,0037

Velocidad en la nueva consulta sin / con el índice: 0,1883 / 0,0035

Por lo tanto, debido a la differency solo marginal entre la vieja pregunta / nueva prefiero seguir utilizando la consulta de edad, pero con el índice. Pero voy a tener esto en cuenta, como si algún día la consulta de edad es demasiado lento:)

Lo que sería interesante para mí es saber, cómo ha llegado hasta la idea de establecer el índice de esta manera? Creo que he publicado esta pregunta, he intentado también con deleted_visible pero en lugar de DATE_CREATED he usado date_published (ya que está en la cláusula-where) ...

Gracias:)

ACTUALIZACIÓN por RolandoMySQLDBA 2011-05-19 13:03

Lo que dio lejos para mí era el WHERE y ORDER BY cláusulas.

En la cláusula WHERE, suprimido (0) y visible (1) son valores estáticos. En la cláusula ORDER BY, el DATE_CREATED era como un objetivo en movimiento entre todas las filas con borrada = 0 y visible = 1. Así, coloqué las variables estáticas frente al índice de primero, luego el blanco móvil última en el índice. Por lo general, eso es parte del principio subyacente de refactorización sentencias SQL. Es necesario índices que apoyen su WHERE, GROUP BY y ORDER cláusulas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a dba.stackexchange
scroll top