Ottimizzare una query SELECT incorporato in MySQL
-
06-09-2019 - |
Domanda
Ok, ecco una query che io sono in esecuzione in questo momento su una tabella che ha 45.000 record ed è 65 MB di dimensioni ... ed è solo per ottenere più grande e più grande (così io devo pensare al futuro prestazioni pure qui ):
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)
E come si potrebbe o non potrebbe immaginare - si soffoca il server MySQL a un punto morto ...
Ciò che fa è - tira semplicemente il numero di nuovi utenti che si sono iscritti, hanno almeno un "completato" il pagamento, tm_completed non è vuota (come è popolato solo per i pagamenti completati), e (Select incorporato) questo membro ha mai avuto un pagamento "a termine" prima - il che significa che è un nuovo membro (solo perché il sistema fa rebills e quant'altro, e questo è l'unico modo per differenziare sorta di tra un utente esistente che ha appena rebilled e un nuovo membro che ma ho fatturato per la prima volta).
Ora, non v'è alcun modo possibile per ottimizzare questa query per utilizzare meno risorse o qualcosa del genere, e di smettere di prendere le mie risorse di MySQL in ginocchio ...?
mi manca qualsiasi informazione per chiarire questo ulteriore? Fatemi sapere ...
EDIT:
Qui ci sono gli indici già su quel tavolo:
PRIMARIA PRIMARY 46757 payment_id
member_id INDICE 23378 member_id
payer_id INDEX 11689 payer_id
coupon_id INDICE 1 coupon_id
Indice tm_added 46757 tm_added, product_id
Indice tm_completed 46757 tm_completed, product_id
Soluzione
Questo tipo di subquery IN
sono un po 'lento in MySQL. Vorrei riformulare in questo modo:
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');
Il controllo 'tm_completed IS NOT NULL
' non è necessaria in quanto ciò è implicito dalla sua condizione BETWEEN
.
assicurarsi di avere un indice anche:
(tm_completed, completed)
Altri suggerimenti
Mi sono divertita a mettere insieme questa soluzione che non richiede una sottoquery:
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;
Evitare di utilizzare IN con una sottoquery; MySQL non ottimizza questi bene (anche se ci sono ottimizzazioni in sospeso in 5.4 e 6.0 per quanto riguarda questo (vedi qui ) riscrittura questo come un join probabilmente vi ottenere un incremento delle prestazioni:.
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
In secondo luogo, avrei dovuto vedere il tuo schema della tabella; stai usando gli indici?