sql >> Base de Datos >  >> RDS >> Mysql

¿Se puede ejecutar más rápido con una gran cantidad de datos [MySQL]?

Mirando tu EXPLAIN salida, me preocupaba que su uso de subconsultas hubiera resultado en un uso subóptimo de índices. yo sentí (sin ninguna justificación, y en esto muy bien puedo estar equivocado) que reescribir usando JOIN podría conducir a una consulta más optimizada.

Para hacer eso, necesitamos entender cuál es la intención de su consulta. Hubiera sido útil si su pregunta la hubiera articulado, pero después de rascarme un poco la cabeza, decidí que su consulta estaba tratando de obtener una lista de todas las demás palabras clave que aparecen en cualquier artículo que contenga alguna palabra clave determinada, junto con un recuento de todos los artículos en los que aparecen esas palabras clave .

Ahora reconstruyamos la consulta por etapas:

  1. Obtener "cualquier artículo que contenga alguna palabra clave determinada " (sin preocuparse por los duplicados):

    SELECT ca2.article_id
    FROM
           career_article_keyword AS ca2
    WHERE
          ca2.keyword_id = 9;
    
  2. Obtener "todas las demás palabras clave que aparecen en [lo anterior] "

    SELECT ca1.keyword_id
    FROM
           career_article_keyword AS ca1
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id;
    
  3. Obtener "[lo anterior], junto con un recuento de todos los artículos en los que aparecen esas palabras clave "

    SELECT ca1.keyword_id, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_article_keyword AS ca0
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ca1.keyword_id
    ORDER BY cnt DESC;
    
  4. Finalmente, queremos agregar a la salida la palabra clave correspondiente de career_keyword tabla:

    SELECT ck.keyword_id, ck.keyword, COUNT(DISTINCT ca0.article_id) AS cnt
    FROM
           career_keywords        AS ck 
      JOIN career_article_keyword AS ca0 USING (keyword_id)
      JOIN career_article_keyword AS ca1 USING (keyword_id)
      JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id)
    WHERE
          ca1.keyword_id <> 9
      AND ca2.keyword_id =  9
    GROUP BY ck.keyword_id -- equal to ca1.keyword_id due to join conditions
    ORDER BY cnt DESC;
    

Una cosa que queda clara de inmediato es que su consulta original hacía referencia a career_keywords dos veces, mientras que esta consulta reescrita hace referencia a esa tabla solo una vez; esto solo podría explicar la diferencia de rendimiento:intente eliminar la segunda referencia (es decir, donde aparece en su primera subconsulta), ya que es completamente redundante allí.

Mirando hacia atrás en esta consulta, podemos ver que se están realizando uniones en las siguientes columnas:

  • career_keywords.keyword_id en ck JOIN ca0

    Esta tabla define PRIMARY KEY (`keyword_id`) , por lo que hay un buen índice que se puede usar para esta combinación.

  • career_article_keyword.article_id en ca1 JOIN ca2

    Esta tabla define UNIQUE KEY `article_id` (`article_id`,`keyword_id`) y, desde article_id es la columna más a la izquierda en este índice, hay un buen índice que se puede usar para esta combinación.

  • career_article_keyword.keyword_id en ck JOIN ca0 y ca0 JOIN ca1

    No hay ningún índice que se pueda usar para esta unión:el único índice definido en esta tabla tiene otra columna, article_id a la izquierda de keyword_id - por lo que MySQL no puede encontrar keyword_id entradas en el índice sin conocer primero el article_id . Le sugiero que cree un nuevo índice que tenga keyword_id como su columna más a la izquierda.

    (La necesidad de este índice también podría haberse determinado directamente al observar su consulta original, donde sus dos consultas más externas realizan uniones en esa columna).