MySQL semble être incapable d'optimiser un select avec une sous-requête GROUP BY et se retrouve dans des temps d'exécution longs. Il doit y avoir une optimisation connue pour un tel scénario commun. Supposons que nous essayons de renvoyer tous les ordres de la base de données, avec un drapeau indiquant si c'est la première commande pour le client.Sous-requête MySQL avec groupe par jointure gauche - optimisation
CREATE TABLE orders (order int, customer int, date date);
La récupération des premières commandes par le client est très rapide.
SELECT customer, min(order) as first_order FROM orders GROUP BY customer;
Cependant, il devient très lent une fois que nous associons cela avec l'ensemble de la commande à l'aide d'un sous-requête
SELECT order, first_order FROM orders LEFT JOIN (
SELECT customer, min(order) as first_order FROM orders GROUP BY customer
) AS first_orders ON orders.order=first_orders.first_order;
J'espère qu'il ya une astuce simple que nous manque, parce qu'il serait autrement 1000x plus rapide à faire
CREATE TEMPORARY TABLE tmp_first_order AS
SELECT customer, min(order) as first_order FROM orders GROUP BY customer;
CREATE INDEX tmp_boost ON tmp_first_order (first_order)
SELECT order, first_order FROM orders LEFT JOIN tmp_first_order
ON orders.order=tmp_first_order.first_order;
EDIT:
Inspiré par @ruakh proposent d option 3, il existe en effet une solution de contournement moins laide utilisant INNER JOIN
et UNION
, qui a des performances acceptables mais ne nécessite pas de tables temporaires. Cependant, c'est un peu spécifique à notre cas et je me demande s'il existe une optimisation plus générique.
SELECT order, "YES" as first FROM orders INNER JOIN (
SELECT min(order) as first_order FROM orders GROUP BY customer
) AS first_orders_1 ON orders.order=first_orders_1.first_order
UNION
SELECT order, "NO" as first FROM orders INNER JOIN (
SELECT customer, min(order) as first_order FROM orders GROUP BY customer
) AS first_orders_2 ON first_orders_2.customer = orders.customer
AND orders.order > first_orders_2.first_order;
Quelques idées: analyser le plan d'exécution (Expliquer la requête); Un index; une sous-requête au lieu d'une jointure à gauche. –
kristox, avez-vous vérifié ma réponse? –