2010-01-25 7 views
1

J'ai une question SQL, liée à this et this question (mais différent). Fondamentalement, je veux savoir comment je peux éviter une requête imbriquée. Disons que j'ai une énorme table d'emplois (jobs) exécutée par une entreprise dans leur histoire. Ces emplois sont caractérisés par l'année, le mois, l'emplacement et le code appartenant à l'outil utilisé pour le travail. De plus, j'ai une table d'outils (tools), traduisant les codes d'outils en descriptions d'outils et d'autres données sur l'outil. Maintenant, ils veulent un site Web où ils peuvent sélectionner l'année, le mois, l'emplacement et l'outil en utilisant une liste déroulante, après quoi les travaux correspondants seront affichés. Je veux remplir le dernier menu déroulant uniquement avec les outils pertinents correspondant à la avant la sélection de l'année, le mois et l'emplacement, donc j'écris la requête imbriquée suivante:Comment éviter une requête SQL imbriquée dans ce cas?

SELECT c.tool_code, t.tool_description 
FROM (
SELECT DISTINCT j.tool_code 
FROM jobs AS j 
WHERE j.year = .... 
     AND j.month = .... 
AND j.location = .... 
) AS c 
LEFT JOIN tools as t 
ON c.tool_code = t.tool_code 
ORDER BY c.tool_code ASC 

Je recours à cette requête imbriquée parce qu'il était beaucoup plus rapide que effectuer un JOIN sur la base de données complète et en sélectionner un. Il a beaucoup diminué mon temps de requête. Mais comme je l'ai lu récemment MySQL nested queries should be avoided at all cost, je me demande si je me trompe dans cette approche. Devrais-je réécrire ma requête différemment? Et comment?

Répondre

2

Non, vous ne devriez pas, votre requête est correcte.

Il suffit de créer un index sur jobs (year, month, location, tool_code) et tools (tool_code) afin que le INDEX FOR GROUP-BY puisse être utilisé.

L'article fourni décrit les sous-requêtes de sous-requêtes (IN (SELECT ...)), et non les requêtes imbriquées (SELECT FROM (SELECT ...)).

Même avec les sous-requêtes, l'article est faux: tout MySQL n'est pas en mesure d'optimiser tous les sous-requêtes, il oeuvre dans IN (SELECT …) prédicats très bien.

Je ne sais pas pourquoi l'auteur a choisi de mettre DISTINCT ici:

SELECT id, name, price 
FROM widgets 
WHERE id IN 
     (
     SELECT DISTINCT widgetId 
     FROM widgetOrders 
     ) 

et pourquoi pensent-ils cela aidera à améliorer les performances, mais étant donné que widgetID est indexé, MySQL va simplement transformer cette requête :

SELECT id, name, price 
FROM widgets 
WHERE id IN 
     (
     SELECT widgetId 
     FROM widgetOrders 
     ) 

dans un index_subquery

Essentiellement, cela est tout comme EXISTS clause: la sous-requête interne sera exécutée une fois par widgets ligne avec le prédicat supplémentaire ajouté:

SELECT NULL 
FROM widgetOrders 
WHERE widgetId = widgets.id 

et arrêt sur la première partie en widgetOrders.

Cette requête:

SELECT DISTINCT w.id,w.name,w.price 
FROM widgets w 
INNER JOIN 
     widgetOrders o 
ON  w.id = o.widgetId 

devront utiliser temporary pour se débarrasser des doublons et sera beaucoup plus lent.

2

Vous pouvez éviter la sous-requête en utilisant GROUP BY, mais si la sous-requête fonctionne mieux, conservez-la.Pourquoi utiliser un LEFT JOIN au lieu d'un JOIN pour rejoindre tools?

+0

Parce que je n'ai pas encore configuré les relations de clé étrangère entre les tables. Après avoir fait cela, je peux passer à un JOIN, mais pour le moment il est toujours possible d'ajouter des tâches avec des codes d'outils encore inconnus à la base de données. J'ai besoin de ces travaux pour toujours apparaître dans le select. – thomaspaulb

Questions connexes