2017-01-10 58 views
0

這是sqlzoo.net這個自我如何加入工作?

下面是表世界的一個問題:

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

問題:

在每個大洲查找最多的國家(按面積),顯示大陸,名稱和區域:

我想了解答案是:

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

該代碼給出:

 
+-------------+-----------+--------+ 
| 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| 
+-------------+-----------+--------+ 

我不明白這是如何工作。我認爲內部選擇應該產生一個包含所有區域的表格,並且外部選擇只選擇最大的(>=)。但是,它是如何過濾到似乎是由大陸組成的列表? y.continent=x.continent AND area>0如何工作?

+1

查詢中沒有JOIN。你有一個相關的子查詢。 –

回答

1

要解釋,我會認爲,只有5個國家的世界表如下:

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

子查詢相匹配的基本查詢的每一行,1-AT-A-時間。這就是所謂的相關子查詢。雖然相關的子查詢運行良好,但如果優化程序無法找出更有效的等效結構,則它們通常被認爲是危險的,因爲它們傾向於產生較差的性能特徵。

下表說明了如何評估數據的邏輯視圖。請注意,數據庫的查詢引擎可能能夠將計劃內部轉換爲數學上等價的內容,但效率更高。

+-------------+--------------+--------+ 
| 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*/ 
+-------------+--------------+--------+ 

根據上述內容,行1,2和5是>=所有的子查詢區域。所以這些被保留,而其他行被丟棄。

請注意,有幾種方法可以編寫產生完全相同結果的子查詢。

正在>=大陸上的所有地區與大陸上的MAX地區相同爲=

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

獲得最大值的另一種方法是在按區域DESC排序時獲取第一行。

WHERE area = (SELECT y.area 
       FROM world y 
       WHERE y.continent=x.continent 
       ORDER BY y.area DESC LIMIT 1) 

但是,要小心以下似乎等效,但不是。

/* The problem here is that only 1 Algeria will happen to be 
    first in the sub-query. Meaning 1 row will be missing from 
    the final result set. */ 
WHERE name = (SELECT y.name 
       FROM world y 
       WHERE y.continent=x.continent 
       ORDER BY y.area DESC LIMIT 1) 

最後,我提到相關的子查詢可能會有性能問題。因此,如果可以的話,通常建議考慮將相關的子查詢重寫爲直接連接到FROM子句中的子查詢。例如。

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) 

注意大寫XTHIS,他們是綁在一起的子查詢與查詢的項目。

根據功能效果(a),子查詢只返回與在查詢級別處理的當前行相關的行。

所以想想這樣。在處理非洲大陸Africa,子查詢基本上是:

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

和,因爲你在外部查詢有WHERE area >= ALL [[that_sub_query]],它只會給你行,其中的面積至少爲最大的面積一樣大那大陸。

或者,更簡潔地說,等於到最大,因爲在給定的組中,沒有一個項目可以同時小於另一個項目並且大於或等於它們全部。


(一)它是如何工作的幕後可能是大大不同,但效果是什麼,我們只關心這裏。