1. 程式人生 > >MySql下實現先排序後分組

MySql下實現先排序後分組

先排序後分組,發現MySql不同的版本有不同的結果,特此記錄。

舉例:要求在shop表中查詢出各型別商店中價格最高的商品。

--表結構--
create table `shop` (
    `id` int (10) PRIMARY KEY,
    `shop_name` varchar (100),
    `item_name` varchar (100),
    `price` int (10)
); 

insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('1','小賣部','醬油','12'
);
insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('2','小賣部','醋','15'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('3','小賣部','脈動','20'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('4','小賣部','沙姜','2'); insert into `shop` (`id`, `shop_name`
, `item_name`,`price`) values('5','超市','豬肉','24');
insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('6','超市','生菜','6'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('7','超市','菜心','5'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('8','連鎖店'
,'生薑','3');
insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('9','超市','牛肉','30'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('10','連鎖店','蒜頭','2'); insert into `shop` (`id`, `shop_name`, `item_name`,`price`) values('11','連鎖店','黃瓜','20');

那麼很自然地就想到了對價格price進行排序然後再根據商店型別shop_name進行分組查詢

select * from (select * from shop order by price desc) a GROUP BY a.shop_name 

這條sql很簡單易懂,接下來我們驗證一下是否正確:

期望結果:
這裡寫圖片描述

MySql 5.7.20下的實際結果:
這裡寫圖片描述

可以看出來實際上得出的結果只是按照表資料的順序,簡單地進行了分組查詢操作,但是這時候我們還不能下結論說這條sql就是錯誤的,我們用另一個數據庫版本(MySql 5.5.57)測試一下。

MySql 5.5.57下的結果:
這裡寫圖片描述

為什麼會有不同的結果?

我們分別檢視一下這條sql在兩個不同版本資料庫的執行計劃:
MySql 5.7.20:
這裡寫圖片描述

MySql 5.5.57:
這裡寫圖片描述

對比可以發現5.7版本的MySql在執行這條sql時缺少了一個derived操作,通過查閱相關資料瞭解到MySql 5.7對子查詢進行了優化,認為子查詢中的order by可以進行忽略,只要Derived table裡不包含如下條件就可以進行優化:

  1. UNION clause
  2. GROUP BY
  3. DISTINCT
  4. Aggregation
  5. LIMIT or OFFSET

這裡把連結放上:5.7中Derived table變形記


最後放上相應的解決辦法:

--方法一,僅適用於低於5.7版本的MySql--
select * from (select * from shop order by price desc) a GROUP BY a.shop_name; 

–方法二–
select * from (select * from shop order by price desc limit 999999) a GROUP BY a.shop_name;

–方法三–
select * from shop a where N > (select count(*) from shop b where b.shop_name = a.shop_name and a.price < b.price) order by a.shop_name,a.price desc;

方法二中使用limit,需要limit的範圍足夠大能包括所有資料,並且每種分類只會顯示一條資料,但是資料較多時執行效率要比方法三快上很多,方法三能夠控制每種分類顯示多少條資料,把N換成需要顯示對應的數字即可。