如何用mysql 取出分組后價格最高的數據 ?
看到這個問題,就想到了用 max 函數和 group by 函數,先 group by 然后取得 max, 但是這樣真的可以嗎? 不可以 !
為什么? 因為要了解 :group by 后,select 取出的默認只是每個分組的第一條數據,但是這條數據和選出的 max,很有可能並不是同一條中的。
比如我們表的內容是這樣的:
嘗試一(失敗)
執行sql語句 select * from test_group group by num; 得到的結果:
執行sql語句 select * , max(price) from test_group group by num; 得到的結果:
發現 max price 的價格和當前這條數據的實際價格根本對應不起來。由此可見: 這種寫法其實是錯誤的,取出來的數據沒有任何含義
嘗試二(失敗)
在網上看到這樣一種寫法:既然分組就取第一條數據,那我們先把表排序,再從排序的表中去分組取第一條數據
select * from test_group order by price desc;
再從這個表中去分組取得第一條數據:select * from (select * from test_group order by price desc) as s group by s.num;
經過實際試驗后,發現結果並不對,看一位博主介紹說,mysql 5.7之后對於這個順序好像進行改變了,所以使用這個語句要特別小心mysql版本。
嘗試三(失敗)
另外還有一種寫法是:先取得每個num下最大的price, 再根據得到的price去找對應的數據
select max(price) from test_group group by num;
select * from test_group where price in (select max(price) from test_group group by num);
看起來可以了,是取出的每組下面最大的數據,但是如果遇到下面這種情況就會有問題啦!
我們加了八八這條數據,屬於 12 組下 價格為130的數據,這樣最終的結果 四四 和 八八都被篩選出來了 !!因為我們篩選出來的max(price) 有130,而八八的價格恰好是130。
優化嘗試三(成功)
可以優化下上面這條sql:我們選擇max(price)時還把對應的組也篩選出來,去判斷兩個的num也是相同的,這就需要做一個表的連接
首先得到num及num下的最高價格:select num, max(price) as max_price from test_group group by num;
將上面的子表與原表進行連接,條件是num和price都要相同,得到結果如下:
select * from test_group as s1 left join (select num, max(price) as max_price from test_group group by num) as s2 on s1.num=s2.num and s1.price=s2.max_price;
然后我們篩選出s2.num 不為空的數據即可
select s1.id as id, s1.num as num, s1.price as price, s1.name as name from test_group as s1 left join (select num, max(price) as max_price from test_group group by num) as s2 on s1.num=s2.num and s1.price=s2.max_price where s2.num is not null;
嘗試四(成功)
我們將表自連接,連接條件是:1的價格<2的價格並且部門相同,這樣的話那些價格最高的不會有數據和它連接,最后篩選2中id is null 的數據就可以了
先將表自連接:select * from test_group s1 left join test_group s2 on s1.price < s2.price and s1.num=s2.num;
篩選 s2.id is null 的數據:
select s1.id as id, s1.num as num, s1.price as price, s1.name as name from test_group s1 left join test_group s2 on s1.price < s2.price and s1.num=s2.num where s2.id is null;
這樣得到的結果和上面也一樣
嘗試五(未經過測試)
使用partition by函數來實現,但是這個函數只在mysql 8版本上運行,我的是5.7,太低了,運行不起來,沒有經過驗證。 這個函數也可以用在hive表中
over(partition by num order by price desc) : 這個語句的意思是先按照 num 進行分組,組內再按照price進行排序
row_number()over(partition by cno order by degree desc) mm: 這個語句的意思是對於上面的結果每組內進行編號,且這列編號列名為 mm
SELECT *
FROM (select sno,cno,degree,
row_number()over(partition by cno order by degree desc) mm
from score)
where mm = 1;