L'optimisation d'une requête SELECT intégrée dans mySQL
-
06-09-2019 - |
Question
Ok, voici une question que je me présente en ce moment sur une table qui a 45.000 dossiers et 65Mo en taille ... et est sur le point de devenir de plus en plus (donc je dois penser à la performance future et ici ):
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)
Et comme vous pouvez ou ne pas imaginer - il étrangle le serveur MySQL à l'arrêt ...
Ce qu'il fait est - il tire simplement le nombre de nouveaux utilisateurs qui se sont inscrits, ont au moins un « terminé » le paiement, tm_completed n'est pas vide (car il est peuplé que pour les paiements réalisés) et (Embedded Select) ce membre n'a jamais eu de « terminé » le paiement avant - ce qui signifie qu'il est un nouveau membre (juste parce que le système ne refacture et ainsi de suite, ce qui est la seule façon de faire la différence en quelque sorte entre un membre existant qui vient de se refacturés et un nouveau membre qui se est facturé pour la première fois).
Maintenant, est-il possible possible d'optimiser cette requête à utiliser moins de ressources ou quelque chose, et d'arrêter de prendre mes ressources mysql à genoux ...?
Suis-je manque toute information pour clarifier cette plus loin? Faites-moi savoir ...
EDIT:
Voici les indices déjà sur la table:
PRIMAIRES 46757 payment_id
member_id INDEX 23378 member_id
payer_id INDEX 11689 payer_id
coupon_id INDEX 1 coupon_id
tm_added INDEX 46757 tm_added, product_id
tm_completed INDEX 46757 tm_completed, product_id
La solution
Ce genre de sous-requêtes IN
sont un peu lent dans MySQL. Je reformule comme ceci:
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');
Le chèque « tm_completed IS NOT NULL
» est pas nécessaire que cela est sous-entendu par votre état de BETWEEN
.
Assurez-vous également que vous avez un index sur:
(tm_completed, completed)
Autres conseils
Je me suis amusée mettre ensemble cette solution qui ne nécessite pas une sous-requête:
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;
Évitez d'utiliser IN avec une sous-requête; MySQL n'optimise pas ces bien (bien qu'il y ait des optimisations en attente de 5.4 et 6.0 en ce qui concerne ce (voir ici ) Réécriture cela comme une jointure va probablement vous obtenir un gain de performance.
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 second lieu, je dois voir votre schéma de la table; vous utilisez des index?