這裏有一個常見的SQL問題: 如何找到當前的每個程序的最新的日誌? 如何從不同類別中找到最常使用的數據?等等. 通常情況這類問題可以被簡化爲"從不同的類別中選取特定的數據".
這裏假設數據庫的表結構及其數據如下所示:
+--------+------------+-------+
| type | variety | price |
+--------+------------+-------+
| apple | gala | 2.79 |
| apple | fuji | 0.24 |
| apple | limbertwig | 2.87 |
| orange | valencia | 3.59 |
| orange | navel | 9.36 |
| pear | bradford | 6.05 |
| pear | bartlett | 2.14 |
| cherry | bing | 2.55 |
| cherry | chelan | 6.33 |
+--------+------------+-------+
查詢每個類別的最小的值
這裏我們先把我們所期望的結果放在這裏
+--------+----------+-------+
| type | variety | price |
+--------+----------+-------+
| apple | fuji | 0.24 |
| orange | valencia | 3.59 |
| pear | bartlett | 2.14 |
| cherry | bing | 2.55 |
當然了,要得到這問題的答案有很多種.這裏用兩種來解釋一下.
一種常見的方式是self-join
,
- 首先根據不同的類別進行分組,
select type, min(price) as minprice
from fruits
group by type;
+--------+----------+
| type | minprice |
+--------+----------+
| apple | 0.24 |
| cherry | 2.55 |
| orange | 3.59 |
| pear | 2.14 |
+--------+----------+
- 第二部是將查詢得到的數據重新連接到相同的table中;因爲第一部分的查詢已經分組了,因此需要將其放在子查詢中並將其連接到table中
select f.type, f.variety, f.price
from (
select type, min(price) as minprice
from fruits group by type
) as x inner join fruits as f on f.type = x.type and f.price = x.minprice;
+--------+----------+-------+
| type | variety | price |
+--------+----------+-------+
| apple | fuji | 0.24 |
| cherry | bing | 2.55 |
| orange | valencia | 3.59 |
| pear | bartlett | 2.14 |
這裏還有一種方式是使用相關子查詢.從效率上將可能有第一點,但是她更加清晰.
select type, variety, price
from fruits
where price = (select min(price) from fruits as f where f.type = fruits.type);
+--------+----------+-------+
| type | variety | price |
+--------+----------+-------+
| apple | fuji | 0.24 |
| orange | valencia | 3.59 |
| pear | bartlett | 2.14 |
| cherry | bing | 2.55 |
通過了子查詢\相關子查詢我們可以比較輕鬆的從大量數據中獲取每一種類的數據.接下來我們更進一步
獲取每種數據的TOP N數據
通常情況下各種數據庫都提供了諸如MAX()
MIN()
這樣的聚合函數來返回單獨的一行數據.但是我們這裏需要返回多條數據.先來看看第一種實現:
select type, variety, price
from fruits
where price = (select min(price) from fruits as f where f.type = fruits.type)
or price = (select min(price) from fruits as f where f.type = fruits.type
and price > (select min(price) from fruits as f2 where f2.type = fruits.type));
+--------+----------+-------+
| type | variety | price |
+--------+----------+-------+
| apple | gala | 2.79 |
| apple | fuji | 0.24 |
| orange | valencia | 3.59 |
| orange | navel | 9.36 |
| pear | bradford | 6.05 |
| pear | bartlett | 2.14 |
| cherry | bing | 2.55 |
| cherry | chelan | 6.33 |
+--------+----------+-------+
可以看到其實思路很簡單,這裏用SQL的僞代碼來表示一下其實現方式
select type, variety, price
from fruits
where price = 價錢最低的 OR
price = 價錢最低的 AND price > 價錢最低的
這種方式可以實現,但是可拓展性比較差.如果現在需要查詢TOP 3,TOP 4 怎麼辦?這裏還有一種方式實現查詢TOP 2
TOP 2 其實質就是 : 價格小於等於第二個便宜的產品.
select type, variety, price
from fruits
where (
select count(*) from fruits as f
where f.type = fruits.type and f.price <= fruits.price
) <= 2;