Optimización de una consulta SELECT incrustado en MySQL
-
06-09-2019 - |
Pregunta
Ok, aquí está una consulta que estoy corriendo en este momento en una tabla que tiene 45.000 registros y es 65 MB de tamaño ... y está a punto de conseguir más grande y más grande (por lo que tengo que pensar en el futuro rendimiento, así que aquí ):
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 - es simplemente tira el número de nuevos usuarios que se inscribieron, tienen al menos una "completa" de pago, tm_completed no está vacío (ya que sólo está poblada por los pagos completados), y (Select incrustado) ese miembro no ha tenido nunca un pago "completado" antes - lo que significa que es un nuevo miembro (sólo porque el sistema hace rebills y todo eso, y esta es la única forma de tipo de diferenciar entre un miembro existente que acaba de conseguir vuelve a facturar y un nuevo miembro que consiguió facturado por primera vez).
Ahora, ¿hay alguna manera posible optimizar esta consulta para utilizar menos recursos o algo así, y para dejar de tomar los recursos de MySQL de rodillas ...?
Me estoy perdiendo ninguna información para aclarar más este asunto? Avisadme ...
EDIT:
Estos son los índices que ya en esa tabla:
PRIMARIA PRIMARIA 46757 payment_id
member_id ÍNDICE 23378 member_id
payer_id ÍNDICE 11689 payer_id
coupon_id ÍNDICE 1 coupon_id
ÍNDICE tm_added 46757 tm_added, product_id
ÍNDICE tm_completed 46757 tm_completed, product_id
Solución
Este tipo de subconsultas IN
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)
Otros consejos
Me divertí armar esta solución que no requiere una sub consulta:
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;
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 (ver aquí ) Reescribiendo esto como una unión 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, tendría que ver el esquema de la tabla; está usando índices?