關於問題
如何查詢組內最大的,最小的,大家或許都知道,無非是min、max的函數使用。可是如何在MySQL中查找組內最好的前兩個,或者前三個?
什么是相關子查詢
在提出對於這個問題的對應方法之前,首先來理解一個概念:相關子查詢。
所謂相關子查詢,就是其查詢的執行依賴於外部查詢。多數情況下是子查詢的where子句中引用了外部查詢的表。執行過程:
- 從外層查詢中取出一個元組,將元組相關列的值傳給內層查詢
- 執行內層查詢,得到子查詢操作的值
- 外查詢根據子查詢返回的結果或結果集得到滿足條件的行
- 然后外層查詢取出下一個元組重復做步驟1-3,直到外層的元組全部處理完畢。
下面會結合例子,在說明組內取值的同時,是如何使用相關子查詢的。
在MySQL中查詢每組中的前N個值
有如上的表,我們需要找出每個類型(type)中最便宜的前兩種水果,我們可以采取這樣的方法:
select type, variety, price
from fruits
where (
select count(*) from fruits as f
where f.type = fruits.type and f.price < fruits.price
) <= 1;
6
1
select type, variety, price
2
from fruits
3
where (
4
select count(*) from fruits as f
5
where f.type = fruits.type and f.price < fruits.price
6
) <= 1;
這個查詢語句就使用到了相關子查詢,結合之前我們提到的相關子查詢的查詢步驟,我們來解析一下這個SQL語句。
從表中首先取出第一條數據,type為apple,variety為gala,price為2.79。相關子查詢中,會將元組相關列的值傳給內層查詢,在子查詢中我們看到
select count(*) from fruits as f
where f.type = fruits.type and f.price < fruits.price
2
1
select count(*) from fruits as f
2
where f.type = fruits.type and f.price < fruits.price
也就是說,實際上這條語句此時應該是這樣
select count(*) from fruits as f
where f.type = apple and f.price < 2.79
2
1
select count(*) from fruits as f
2
where f.type = apple and f.price < 2.79
即,在apple這個類別中,價格比2.79還要低的元組的數量。
我們再看全貌
select type, variety, price
from fruits
where (
select count(*) from fruits as f
where f.type = apple and f.price < 2.79
) <= 1;
6
1
select type, variety, price
2
from fruits
3
where (
4
select count(*) from fruits as f
5
where f.type = apple and f.price < 2.79
6
) <= 1;
可以知道,apple中價格低於2.79的只有fuji這個元組,即值為1,滿足子條件<=1的條件,所以該元組會被輸出。
如果不好理解,這樣來看。其實這個子條件的意思就是在說,
我提取一個元組A,如果這個元組A所屬的類別中,價格比元組A低的最多只有一個,那么就輸出(即它本身是最便宜的,那么比它價格還低的就只有0個;或者它是第二便宜的,那么價格比它低的就只有1個。這樣一來,只要數量條件為<=1,那么就得到了最便宜的兩種水果)
按照相關子查詢的執行順序,第一個元組符合條件,被輸出。接下來,把第二個元組的值帶入,如果符合條件則輸出,接着第三個元組第四個元組... 直到所有的元組帶入篩選完畢,整個查詢過程也就結束了。
什么,你說價格排列沒有順序?加上order就可以了。
select type, variety, price
from fruits
where (
select count(*) from fruits as f
where f.type = fruits.type and f.price < fruits.price
) <= 1
order by fruits.type, fruits.price
1
select type, variety, price
2
from fruits
3
where (
4
select count(*) from fruits as f
5
where f.type = fruits.type and f.price < fruits.price
6
) <= 1
7
order by fruits.type, fruits.price
