sql如何先排序再去重


場景

有一張得分表(score),記錄了用戶每次的得分,同一個人可能有多個得分。

id name score
1 tom 45
2 jack 78
3 tom 34
. . .

需求:找出分數最高的前5個人。

SQL1

首先我們寫個最簡單的sql:

select 
	id, name, score
from 
	score
order by 
	score desc
limit 5;

如果sql這樣寫,結果可能是:

id name score
2 jack 78
1 tom 45
3 tom 34

排序了,但是沒有去重

SQL2

那么我們加上去重:

select 
	distinct name
from 
	score
order by 
	score desc
limit 5;

首先第一點是這個sql未必能執行。在一些數據庫版本,這個sql可以被執行,在一些版本則會提示你order by的字段必須在distinct中存在(見SQL3)。

但是即使能執行,這個sql也得不到預期結果。原因是distinct優先於order by 被數據庫執行。

在執行distinct name的時候,如上文中的數據。是取id=1的數據,還是id=3的數據呢?其實這是數據庫自行決定的。因此,可能會不正確選擇數據。

比如真的執行這個sql,可能去重的結果是:

id name score
2 jack 78
3 tom 34

然后再執行一個order by,就會認為第一名是jack78分,第二名是tom34分。然而其實tom應該是45分,這個45分就在數據庫執行distinct的時候被錯誤的丟棄了,畢竟先執行distinct的時候不知道你到底要哪個數據。

SQL3

那么我們把score加入select中呢?

select 
	distinct name, score
from 
	score
order by 
	score desc
limit 5;

很明顯,這樣寫的執行結果和我們預期不符。因為如果寫:distinct name,score實際上是對name和score一起去重。比如name都是jack,score都是45。那么這行就會被去掉。

但是問題是正因為把score當做去重的條件了。所以對於同名的人,比如都叫tom,會因為其有兩個分數,導致不能被去重,從而保留兩行記錄。結果就是好像沒有去重。

SQL4

那我不用distinct,用group by進行去重可以嗎?

select 
	name
from 
	score
group by
	name
order by 
	score desc
limit 5;

也不行,因為在group by的時候,數據庫還是不知道對兩行name一樣的數據,究竟應該留下哪一行。

SQL5

正確的寫法:

select 
	name
from 
	score
group by
	name
order by 
	max(score) desc
limit 5;

這樣寫,在執行group by的時候,數據庫就知道要保留score最大的那一行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM