2017-01-10 1 views
0

Ceci est un problème de sqlzoo.netComment cette auto-jonction fonctionne-t-elle?

Voici le monde de la table:

 
+-------------+-----------+---------+ 
| name  | continent | area | 
+-------------+-----------+---------+ 
| Afghanistan | Asia  | 652230 | 
| Albania  | Europe | 2831741 | 
| Algeria  | Africa | 28748 | 
| ...   | ...  | ...  | 
+-------------+-----------+---------+ 

La question:

Trouver le plus grand pays (par région) sur chaque continent, montrent le continent, le nom et la région:

la réponse que je suis en train de comprendre est:

SELECT continent, name, area 
FROM world x 
WHERE area >= ALL (SELECT area 
        FROM world y 
        WHERE y.continent=x.continent 
        AND area>0) 

Ce code donne:

 
+-------------+-----------+--------+ 
| continent | name | area | 
+-------------+-----------+--------+ 
|Africa  | Algeria | 2381741| 
|Oceania  | Australia | 7692024| 
|South America| Brazil | 8515767| 
|North America| Canada | 9984670| 
|Asia   | China  | 9596961| 
|Caribbean | Cuba  | 109884| 
|Europe  | France | 640679| 
|Eurasia  | Russia |17125242| 
+-------------+-----------+--------+ 

Je ne comprends pas comment cela fonctionne. Je pense que la sélection interne devrait produire une table qui contient toutes les zones et la sélection externe ne choisit que la plus grande (>=). Mais comment cela se résume-t-il à une liste qui semble avoir été groupée par continent? Comment fonctionne y.continent=x.continent AND area>0?

+1

Il n'y a pas de JOINs dans votre requête. Vous avez une sous-requête corrélée. –

Répondre

1

Pour expliquer, je suppose une table mondiale avec seulement 5 pays comme suit:

Algeria  2381741 
Australia 7692024 
South Africa 1221037 
New Zealand 268021 
/*And to make it a little interesting:*/ 
Algeria Twin 2381741 

La sous-requête est adaptée à chaque ligne de la requête de base, 1-à-un-temps. C'est ce qu'on appelle une sous-requête corrélée. Bien que les sous-requêtes corrélées fonctionnent bien, elles sont généralement considérées comme dangereuses en raison de leur tendance à produire des caractéristiques de performance médiocres si l'optimiseur ne peut pas trouver une structure plus efficace et équivalente.

Le tableau suivant illustre une vue logique de la manière dont les données seront évaluées. Notez que le moteur de recherche de la base de données pourrait être capable de transformer le plan en quelque chose de mathématiquement équivalent, mais beaucoup plus efficace.

+-------------+--------------+--------+ 
| continent | name  | area | 
+-------------+--------------+--------+ 
|Africa  | Algeria  | 2381741| >= ALL(/*y.continent='Africa'*/ 
               2381741, /*Alegria*/ 
               1221037, /*South Africa*/ 
               2381741) /*Alegria Twin*/ 
|Oceania  | Australia | 7692024| >= ALL(/*y.continent='Oceania'*/ 
               7692024, /*Australia*/ 
               268021) /*New Zealand*/ 
|Africa  | South Africa | 1221037| >= ALL(/*y.continent='Africa'*/ 
               2381741, /*Alegria*/ 
               1221037, /*South Africa*/ 
               2381741) /*Alegria Twin*/ 
|Oceania  | New Zealand | 268021| >= ALL(/*y.continent='Oceania'*/ 
               7692024, /*Australia*/ 
               268021) /*New Zealand*/ 
|Africa  | Algeria Twin | 2381741| >= ALL(/*y.continent='Africa'*/ 
               2381741, /*Alegria*/ 
               1221037, /*South Africa*/ 
               2381741) /*Alegria Twin*/ 
+-------------+--------------+--------+ 

De ce qui précède, les lignes 1, 2 et 5 sont >= toutes les zones sous-requête. Donc, ceux-ci sont conservés, tandis que les autres rangées sont jetées.

Notez qu'il existe plusieurs façons d'écrire la sous-requête qui produira exactement les mêmes résultats.

Étant >= toutes les zones sur un continent est la même que = la zone MAX sur le continent.

WHERE area = (SELECT MAX(y.area) 
       FROM world y 
       WHERE y.continent=x.continent) 

Une autre façon d'obtenir le maximum est d'obtenir la première ligne lors de la commande par zone DESC. Cependant, faites attention à ce qui suit qui semble équivalent, mais ne l'est pas. Enfin, j'ai mentionné que les sous-requêtes corrélées peuvent avoir des problèmes de performance. Il est donc généralement conseillé d'envisager de réécrire une sous-requête corrélée en une sous-requête qui se joint directement à la sous-requête de la clause FROM si vous le pouvez. Par exemple.

SELECT x.contient, x.name, x.area 
FROM world x 
     INNER JOIN (
      SELECT MAX(y.area) as max_area, y.continent 
      FROM world y 
      GROUP BY y.continent 
     ) z ON 
      x.continent = z.continent 
     AND x.area = z.max_area 
1
SELECT THIS, name, area 
FROM world X 
WHERE area >= ALL (SELECT area 
        FROM world y 
        WHERE y.continent = X.THIS 
        AND area > 0) 

Notez la majuscule X et THIS, ils sont les éléments qui lient ensemble la sous-requête avec la requête.

En termes d'effet fonctionnel (a), la sous-requête renvoie uniquement les lignes liées à la ligne courante en cours de traitement au niveau de la requête.

Alors pensez-y de cette façon. Lors du traitement du continent Africa, la sous-requête est essentiellement:

SELECT area 
FROM world y 
WHERE y.continent = 'Africa' 
    AND area > 0 

et, parce que vous avez WHERE area >= ALL [[that_sub_query]] dans la requête externe, il vous donnera uniquement les lignes où la zone est au moins aussi grande que la plus grande zone de ce continent.

Ou, plus succinctement, égal à au plus grand puisque, dans un groupe donné, aucun article ne peut être simultanément plus petit qu'un autre et plus grand ou égal à tous.


(a) Comment ça marche sous les couvertures peuvent être largement différent, mais l'effet est ce qui nous intéresse ici.