2009-07-07 16 views
6

Creé un foro personalizado para mi sitio utilizando MySQL. La página de listado es esencialmente una tabla con las siguientes columnas: Tema, Última actualización y # Respuestas.Optimizar la consulta de MySQL para evitar "Uso de; Uso temporal; Uso de filesort"

mesa El PP tiene las siguientes columnas:

id 
name 
body 
date 
topic_id 
email 

Un tema tiene la topic_id de "0", y respuestas tener la topic_id de su Tema principal.

SELECT SQL_CALC_FOUND_ROWS 
    t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM 
    wp_pod_tbl_forum t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r ON (r.topic_id = t.id) 
WHERE 
    t.topic_id = 0 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

Hay alrededor de 2.100 artículos en total en esta tabla, y las consultas suelen tardar unos increíbles 6 segundos. Agregué un ÍNDICE a la columna "topic_id", pero eso no ayudó mucho. ¿Hay alguna forma de acelerar esta consulta sin hacer una reestructuración significativa?

EDIT: no del todo funcionando todavía. Parece que no puedo obtener los ejemplos a continuación para que funcionen correctamente.

Respuesta

7
SELECT id, name, last_reply, replies 
FROM (
     SELECT topic_id, MAX(date) AS last_reply, COUNT(*) AS replies 
     FROM wp_pod_tbl_forum 
     GROUP BY 
       topic_id 
     ) r 
JOIN wp_pod_tbl_forum t 
ON  t.topic_id = 0 
     AND t.id = r.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     date DESC 
LIMIT 0, 20 

Si la tabla es MyISAM o id no es un PRIMARY KEY, es necesario crear un ONDEX compuesto sobre (topic_id, id).

Si la tabla es InnoDB y id es una PRIMARY KEY, un índice sólo en (topic_id) hará (id se añadirá implícitamente al índice).

actualización

Esta consulta muy probablemente será aún más eficaz, siempre y cuando tenga índices en (topic_id, id) y (date, id):

Lee este artículo en mi blog para los detalles de rendimiento:

Esta consulta se completa en 30 ms en una muestra de datos 100,000 filas: Es necesario

SELECT id, name, last_reply, 
     (
     SELECT COUNT(*) 
     FROM wp_pod_tbl_forum fc 
     WHERE fc.topic_id = fl.topic_id 
     ) AS replies 
FROM (
     SELECT topic_id, date AS last_reply 
     FROM wp_pod_tbl_forum fo 
     WHERE id = (
       SELECT id 
       FROM wp_pod_tbl_forum fp 
       WHERE fp.topic_id = fo.topic_id 
       ORDER BY 
         fp.date DESC, fp.id DESC 
       LIMIT 1 
       ) 
       AND fo.topic_id <> 0 
     ORDER BY 
       fo.date DESC, fo.id DESC 
     LIMIT 20 
     ) fl 
JOIN wp_pod_tbl_forum ft 
ON  ft.id = fl.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     last_reply DESC, id DESC 
LIMIT 20 

Ambos índices para esta consulta para ser eficiente.

Si la tabla es InnoDByid es una PRIMARY KEY, a continuación, se puede omitir Identificación del indexes anteriormente.

+0

La 'fecha' de columna en la lista de campos es ambigua ..? – Matt

+0

@Matt: ver la actualización – Quassnoi

+0

@Quassnoi - ¿puedes explicar lo que está pasando? ¿El "UNION ALL" reemplaza "last_reply" con "date" si el tema no tiene respuestas? – Matt

1

Es posible que desee dividirlo en un conjunto de subconsultas (como consultas internas). Que necesitaría el esquema para jugar de verdad, pero si

SELECT t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM (
    SELECT (id, name, date) 
    FROM wp_pod_tbl_forum 
    WHERE topic_id = 0 
) as t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r 
WHERE 
    r.topic_id = t.id 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

que pueden ayudar a acelerar hacia arriba un poco, puede incluso no ser la mejor respuesta (pueden existir errores).

Hay muchas maneras de hacerlo, pero lo más importante cuando el ajuste de SQL es reducir cada conjunto tanto como sea posible antes de realizar una operación.