2009-05-30 17 views
5

Ok, aquí hay una consulta que estoy ejecutando ahora en una tabla que tiene 45,000 registros y tiene 65MB de tamaño ... y está a punto de hacerse más y más grande (entonces tengo que pensar del futuro rendimiento, así aquí):Optimización de una consulta SELECT incrustada en mySQL

SELECT count(payment_id) as signup_count, sum(amount) as signup_amount 
FROM payments p 
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30' 
AND completed > 0 
AND tm_completed IS NOT NULL 
AND member_id NOT IN (SELECT p2.member_id FROM payments p2 WHERE p2.completed=1 AND p2.tm_completed < '2009-05-01' AND p2.tm_completed IS NOT NULL GROUP BY p2.member_id) 

Y a medida que podría o no podría imaginar - que ahoga el servidor MySQL a un punto muerto ...

lo que hace es - simplemente tira de la serie de los nuevos usuarios que se registraron, tienen al menos un pago "completado", tm_completed no está vacío (ya que solo se llena para los pagos completados), y (el Select incrustado) ese miembro nunca ha tenido un "com se ha completado el pago antes, lo que significa que es un miembro nuevo (solo porque el sistema se vuelve a facturar y otras cosas, y esta es la única forma de diferenciar entre un miembro existente que acaba de ser reembolsado y un nuevo miembro que se facturó por primera vez) .

Ahora, ¿hay alguna manera posible de optimizar esta consulta para usar menos recursos o algo así, y dejar de tomar mis recursos de mysql de rodillas ...?

¿Me falta alguna información para aclarar esto más? Avisadme ...

EDIT:

Estos son los índices que ya en esa tabla:

PRIMARIA PRIMARIA 46757 payment_id

ÍNDICE member_id 23378 member_id

ÍNDICE payer_id 11689 payer_id

cupón_id INDICE 1 coupon_id

ÍNDICE tm_added 46757 tm_added, product_id

tm_completed ÍNDICE 46757 tm_completed, product_id

+0

¿tiene índices en los campos donde se utilizan argumentos de búsqueda – James

Respuesta

7

Ese tipo de IN subconsultas son un poco lentos en MySQL. Me gustaría reformular así:

SELECT COUNT(1) AS signup_count, SUM(amount) AS signup_amount 
FROM payments p 
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30' 
AND completed > 0 
AND NOT EXISTS (
      SELECT member_id 
      FROM payments 
      WHERE member_id = p.member_id 
      AND completed = 1 
      AND tm_completed < '2009-05-01'); 

El cheque 'tm_completed IS NOT NULL' no es necesario, ya que está implícito en su condición BETWEEN.

También asegúrese de que tiene un índice en:

(tm_completed, completed) 
+0

me golpearon? al golpe; +1 a la velocidad –

+0

Wow ...No sabía que era solo un pequeño cambio de lo que ya había hecho, simplemente reemplazando el "IN" por "EXISTS" ... ¡gracias! –

2

Evitar el uso de IN con una subconsulta; MySQL no optimiza estas bien (aunque hay pendientes optimizaciones en 5,4 y 6,0 con respecto a este (véase here) Reescribiendo esto como un join probablemente se obtiene un aumento de rendimiento:.

SELECT count(payment_id) as signup_count, sum(amount) as signup_amount 
FROM payments p 
LEFT JOIN (SELECT p2.member_id 
      FROM payments p2 
      WHERE p2.completed=1 
      AND p2.tm_completed < '2009-05-01' 
      AND p2.tm_completed IS NOT NULL 
      GROUP BY p2.member_id) foo 
ON p.member_id = foo.member_id AND foo.member_id IS NULL 
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30' 
AND completed > 0 
AND tm_completed IS NOT NULL 

En segundo lugar, habría que ver el esquema de la tabla; está usando índices

7

me había divertido armar esta solución que no requiere una subconsulta:

SELECT count(p1.payment_id) as signup_count, 
     sum(p1.amount)  as signup_amount 

    FROM payments p1 
     LEFT JOIN payments p2 
     ON p1.member_id = p2.member_id 
    AND p2.completed = 1 
    AND p2.tm_completed < date '2009-05-01' 

WHERE p1.completed > 0 
    AND p1.tm_completed between date '2009-05-01' and date '2009-05-30' 
    AND p2.member_id IS NULL; 
+1

Esta técnica es confiablemente efectiva especialmente en mysql (que históricamente ha tenido problemas con las subconsultas). – dkretz

+0

Me gustó esta respuesta también ... al parecer, cuando ejecuto EXPLAIN en las dos respuestas que elegí aquí, obtengo el mismo uso de rendimiento/recursos (aproximadamente 12,000 veces más rápido que cuando se usa la subconsulta "IN"). ¡Increíble! Gracias... –

Cuestiones relacionadas