国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > MySQL > 正文

mysql數據庫分組,GROUP BY,查詢實例

2024-07-24 12:36:55
字體:
來源:轉載
供稿:網友
mysql數據庫分組為分三種方式,使用臨時表實現 GROUP BY、緊湊(Tight)索引掃描實現 GROUP BY、松散(Loose)索引掃描實現 GROUP BY,但我常用的是使用了

1.使用松散(Loose)索引掃描實現 GROUP BY

何謂松散索引掃描實現 GROUP BY 呢?實際上就是當 MySQL 完全利用索引掃描來實現 GROUP BY 的時候,并不需要掃描所有滿足條件的索引鍵即可完成操作得出結果。

下面我們通過一個示例來描述松散索引掃描實現 GROUP BY,在示例之前我們需要首先調整一下 group_message 表的索引,將 gmt_create 字段添加到 group_id 和 user_id 字段的索引中,代碼如下:

  1.  sky@localhost : example 08:49:45> create index idx_gid_uid_gc 
  2.  
  3.  -> on group_message(group_id,user_id,gmt_create); 
  4.  
  5.  Query OK, rows affected (0.03 sec) 
  6.  
  7.  Records: 96 Duplicates: 0 Warnings: 0 
  8.  
  9.  sky@localhost : example 09:07:30> drop index idx_group_message_gid_uid 
  10.  
  11.  -> on group_message; 
  12.  
  13.  Query OK, 96 rows affected (0.02 sec) 
  14.  
  15.  Records: 96 Duplicates: 0 Warnings: 0 

然后再看如下 Query 的執行計劃,代碼如下:

  1.  sky@localhost : example 09:26:15> EXPLAIN 
  2.  
  3.  -> SELECT user_id,max(gmt_create) 
  4.  
  5.  -> FROM group_message 
  6.  
  7.  -> WHERE group_id < 10 
  8.  
  9.  -> GROUP BY group_id,user_idG 
  10.  
  11.  *************************** 1. row *************************** 
  12.  
  13.  id: 1 
  14.  
  15.  select_type: SIMPLE 
  16.  
  17.  table: group_message 
  18.  
  19.  type: range 
  20.  
  21.  possible_keys: idx_gid_uid_gc 
  22.  
  23.  key: idx_gid_uid_gc 
  24.  
  25.  key_len: 8 
  26.  
  27.  ref: NULL 
  28.  
  29.  rows: 4 
  30.  
  31.  Extra: Using where; Using index for group-by 
  32.  
  33.  1 row in set (0.00 sec) 

我們看到在執行計劃的 Extra 信息中有信息顯示“Using index for group-by”,實際上這就是告訴我們,MySQL Query Optimizer 通過使用松散索引掃描來實現了我們所需要的 GROUP BY 操作。

下面這張圖片描繪了掃描過程的大概實現,要利用到松散索引掃描實現 GROUP BY,需要至少滿足以下幾個條件:

◆GROUP BY 條件字段必須在同一個索引中最前面的連續位置;

◆在使用GROUP BY 的同時,只能使用 MAX 和 MIN 這兩個聚合函數;

◆如果引用到了該索引中 GROUP BY 條件之外的字段條件的時候,必須以常量形式存在;

為什么松散索引掃描的效率會很高?

因為在沒有WHERE子句,也就是必須經過全索引掃描的時候, 松散索引掃描需要讀取的鍵值數量與分組的組數量一樣多,也就是說比實際存在的鍵值數目要少很多。而在WHERE子句包含范圍判斷式或者等值表達式的時候,松散索引掃描查找滿足范圍條件的每個組的第1個關鍵字,并且再次讀取盡可能最少數量的關鍵字。

2.使用緊湊(Tight)索引掃描實現 GROUP BY

緊湊索引掃描實現 GROUP BY 和松散索引掃描的區別主要在于他需要在掃描索引的時候,讀取所有滿足條件的索引鍵,然后再根據讀取惡的數據來完成 GROUP BY 操作得到相應結果,代碼如下:

  1.   sky@localhost : example 08:55:14> EXPLAIN 
  2.  
  3.  -> SELECT max(gmt_create) 
  4.  
  5.  -> FROM group_message 
  6.  
  7.  -> WHERE group_id = 2 
  8.  
  9.  -> GROUP BY user_idG 
  10.  
  11.  *************************** 1. row *************************** 
  12.  
  13.  id: 1 
  14.  
  15.  select_type: SIMPLE 
  16.  
  17.  table: group_message 
  18.  
  19.  type: ref 
  20.  
  21.  possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc 
  22.  
  23.  key: idx_gid_uid_gc 
  24.  
  25.  key_len: 4 
  26.  
  27.  ref: const 
  28.  
  29.  rows: 4 
  30.  
  31.  Extra: Using where; Using index 
  32.  
  33.  1 row in set (0.01 sec) 

這時候的執行計劃的 Extra 信息中已經沒有“Using index for group-by”了,但并不是說 MySQL 的 GROUP BY 操作并不是通過索引完成的,只不過是需要訪問 WHERE 條件所限定的所有索引鍵信息之后才能得出結果,這就是通過緊湊索引掃描來實現 GROUP BY 的執行計劃輸出信息.

在 MySQL 中,MySQL Query Optimizer 首先會選擇嘗試通過松散索引掃描來實現 GROUP BY 操作,當發現某些情況無法滿足松散索引掃描實現 GROUP BY 的要求之后,才會嘗試通過緊湊索引掃描來實現.

當 GROUP BY 條件字段并不連續或者不是索引前綴部分的時候,MySQL Query Optimizer 無法使用松散索引掃描,設置無法直接通過索引完成 GROUP BY 操作,因為缺失的索引鍵信息無法得到,但是,如果 Query 語句中存在一個常量值來引用缺失的索引鍵,則可以使用緊湊索引掃描完成 GROUP BY 操作,因為常量填充了搜索關鍵字中的“差距”,可以形成完整的索引前綴,這些索引前綴可以用于索引查找,而如果需要排序GROUP BY結果,并且能夠形成索引前綴的搜索關鍵字,MySQL還可以避免額外的排序操作,因為使用有順序的索引的前綴進行搜索已經按順序檢索到了所有關鍵字.

3.使用臨時表實現 GROUP BY

MySQL 在進行 GROUP BY 操作的時候要想利用所有,必須滿足 GROUP BY 的字段必須同時存放于同一個索引中,且該索引是一個有序索引(如 Hash 索引就不能滿足要求),而且,并不只是如此,是否能夠利用索引來實現 GROUP BY 還與使用的聚合函數也有關系.

前面兩種 GROUP BY 的實現方式都是在有可以利用的索引的時候使用的,當 MySQL Query Optimizer 無法找到合適的索引可以利用的時候,就不得不先讀取需要的數據,然后通過臨時表來完成 GROUP BY 操作,代碼如下:

  1.  sky@localhost : example 09:02:40> EXPLAIN 
  2.  
  3.  -> SELECT max(gmt_create) 
  4.  
  5.  -> FROM group_message 
  6.  
  7.  -> WHERE group_id > 1 and group_id < 10 
  8.  -> GROUP BY user_idG  
  9.  
  10.  *************************** 1. row *************************** 
  11.  
  12.  id: 1 
  13.  
  14.  select_type: SIMPLE 
  15.  
  16.  table: group_message 
  17.  
  18.  type: range 
  19.  
  20.  possible_keys: idx_group_message_gid_uid,idx_gid_uid_gc 
  21.  
  22.  key: idx_gid_uid_gc 
  23.  
  24.  key_len: 4 
  25.  
  26.  ref: NULL 
  27.  
  28.  rows: 32 
  29.  
  30.  Extra: Using where; Using index; Using temporary; Using filesort 

這次的執行計劃非常明顯的告訴我們 MySQL 通過索引找到了我們需要的數據,然后創建了臨時表,又進行了排序操作,才得到我們需要的 GROUP BY 結果.

當 MySQL Query Optimizer 發現僅僅通過索引掃描并不能直接得到 GROUP BY 的結果之后,他就不得不選擇通過使用臨時表然后再排序的方式來實現 GROUP BY了.

在這樣示例中即是這樣的情況,group_id 并不是一個常量條件,而是一個范圍,而且 GROUP BY 字段為 user_id,所以 MySQL 無法根據索引的順序來幫助 GROUP BY 的實現,只能先通過索引范圍掃描得到需要的數據,然后將數據存入臨時表,然后再進行排序和分組操作來完成 GROUP BY.

講了這么多其實最簡單的就是,查詢dedecms,織夢,程序的欄目標題表,以欄目id分組,代碼如下:

  1. SELECT * 
  2. FROM `dede_archives` 
  3. GROUP BY `typeid` 
  4. LIMIT 0 , 30 

這樣即可了,一些相關group by 實例,代碼如下,--按某一字段分組取最大(小)值所在行的數據,數據如下:

  1. name val memo  
  2. a 2 a2(a的第二個值)  
  3. a 1 a1--a的第一個值  
  4. a 3 a3:a的第三個值  
  5. b 1 b1--b的第一個值  
  6. b 3 b3:b的第三個值  
  7. b 2 b2b2b2b2  
  8. b 4 b4b4  
  9. b 5 b5b5b5b5b5  
  10. */ 
  11. --創建表并插入數據: 
  12. create table tb(name varchar(10),val int,memo varchar(20))  
  13. insert into tb values('a', 2, 'a2(a的第二個值)')  
  14. insert into tb values('a', 1, 'a1--a的第一個值')  
  15. insert into tb values('a', 3, 'a3:a的第三個值')  
  16. insert into tb values('b', 1, 'b1--b的第一個值')  
  17. insert into tb values('b', 3, 'b3:b的第三個值')  
  18. insert into tb values('b', 2, 'b2b2b2b2')  
  19. insert into tb values('b', 4, 'b4b4')  
  20. insert into tb values('b', 5, 'b5b5b5b5b5')  
  21. go 
  22. --一、按name分組取val最大的值所在行的數據。 
  23. --方法1:select a.* from tb a where val = (select max(val) from tb where name = a.name) order by a.name  
  24. --方法2:  
  25. select a.* from tb a where not exists(select 1 from tb where name = a.name and val > a.val)  
  26. --方法3:  
  27. select a.* from tb a,(select name,max(val) val from tb group by name) b where a.name = b.name and a.val = b.val order by a.name  
  28. --方法4:  
  29. select a.* from tb a inner join (select name , max(val) val from tb group by name) b on a.name = b.name and a.val = b.val order by a.name  
  30. --方法5  
  31. select a.* from tb a where 1 > (select count(*) from tb where name = a.name and val > a.val ) order by a.name  
  32. /*  
  33. name val memo  
  34. ---------- ----------- --------------------  
  35. a 3 a3:a的第三個值  
  36. b 5 b5b5b5b5b5 
  37. */ 

本人推薦使用1,3,4,結果顯示1,3,4效率相同,2,5效率差些,不過我3,4效率相同毫無疑問,1就不一樣了,想不搞了.

二、按name分組取val最小的值所在行的數據。

方法1:select a.* from tb a where val = (select min(val) from tb where name = a.name) order by a.name

方法2:select a.* from tb a where not exists(select 1 from tb where name = a.name and val < a.val)

方法3:select a.* from tb a,(select name,min(val) val from tb group by name) b where a.name = b.name and a.val = b.val order by a.name

方法4:select a.* from tb a inner join (select name , min(val) val from tb group by name) b on a.name = b.name and a.val = b.val order by a.name

方法5:select a.* from tb a where 1 > (select count(*) from tb where name = a.name and val < a.val) order by a.name

  1. name val memo  
  2. ---------- ----------- --------------------  
  3. a 1 a1--a的第一個值  
  4. b 1 b1--b的第一個值 

三、按name分組取第一次出現的行所在的數據.

  1. select a.* from tb a where val = (select top 1 val from tb where name = a.nameorder by a.name  
  2. /*  
  3. name val memo  
  4. ---------- ----------- --------------------  
  5. a 2 a2(a的第二個值)  
  6. b 1 b1--b的第一個值  
  7. */ 

四、按name分組隨機取一條數據.

  1. select a.* from tb a where val = (select top 1 val from tb where name = a.name order by newid()) order by a.name/*  
  2. name val memo  
  3. ---------- ----------- --------------------  
  4. a 1 a1--a的第一個值  
  5. b 5 b5b5b5b5b5 
  6. */ 

五、按name分組取最小的兩個(N個)val

  1. select a.* from tb a where 2 > (select count(*) from tb where name = a.name and val < a.val ) order by a.name,a.valselect a.* from tb a where val in (select top 2 val from tb where name=a.name order by val) order by a.name,a.val  
  2. select a.* from tb a where exists (select count(*) from tb where name = a.name and val < a.val having Count(*) < 2) order by a.name  
  3. /*  
  4. name val memo  
  5. ---------- ----------- --------------------  
  6. a 1 a1--a的第一個值  
  7. a 2 a2(a的第二個值)  
  8. b 1 b1--b的第一個值  
  9. b 2 b2b2b2b2 
  10. */ 

六、按name分組取最大的兩個(N個)val

  1. select a.* from tb a where 2 > (select count(*) from tb where name = a.name and val > a.val ) order by a.name,a.val  
  2. select a.* from tb a where val in (select top 2 val from tb where name=a.name order by val descorder by a.name,a.val  
  3. select a.* from tb a where exists (select count(*) from tb where name = a.name and val > a.val having Count(*) < 2) order by a.name  
  4. /*  Vevb.com 
  5. name val memo  
  6. ---------- ----------- --------------------  
  7. a 2 a2(a的第二個值)  
  8. a 3 a3:a的第三個值  
  9. b 4 b4b4  
  10. b 5 b5b5b5b5b5  
  11. */ 

七,假如整行數據有重復,所有的列都相同,例如下表中的第5,6兩行數據完全相同,按name分組取最大的兩個(N個)val,數據如下:

  1. name val memo  
  2. a 2 a2(a的第二個值)  
  3. a 1 a1--a的第一個值  
  4. a 1 a1--a的第一個值  
  5. a 3 a3:a的第三個值  
  6. a 3 a3:a的第三個值  
  7. b 1 b1--b的第一個值  
  8. b 3 b3:b的第三個值  
  9. b 2 b2b2b2b2  
  10. b 4 b4b4  
  11. b 5 b5b5b5b5b5

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 新宁县| 苏州市| 万源市| 独山县| 固阳县| 华容县| 全椒县| 南通市| 钟山县| 萝北县| 保定市| 大邑县| 西青区| 广东省| 聂拉木县| 当涂县| 涟源市| 叙永县| 云阳县| 南投市| 梁河县| 潞西市| 孝感市| 石屏县| 丹凤县| 稻城县| 东乡县| 呼和浩特市| 乐陵市| 静安区| 甘肃省| 汤原县| 上栗县| 镇雄县| 梨树县| 孝感市| 泽州县| 丰顺县| 云阳县| 马山县| 无为县|