8.2.1.16 GROUP BY Optimization
最大的方式來滿足一個GROUP BY 子句是來scan 整個表,建立一個新的臨時表,
所有的記錄從每個組是連續的, 然後使用臨時表來發現組和應用聚合函數(如果有的話),
在一些情況下, MySQL 是能夠做的更好,避免建立臨時表通過使用索引通路。
最重要的前提用于使用索引用于GROUP BY 是,所有的GROUP BY 子句關聯關聯相同索引的屬性,
索引存儲它的keys 按順序(舉個例子, 這裡有一個BTREE index 不是一個HASH index)。
是否使用 臨時表可以通過索引通路替換 依賴于 索引那部分被使用,
條件指定的那些部分, 和選擇的聚合函數。
這裡有兩種方式來執行一個GROUP BY 查詢通過索引通路, 細節在下面章節描述。
在第一種方法, grouping 操作是應用和所有的範圍謂詞一起(如果有的話).
第2個方法首先執行一個range scan,然後彙總結果tuples。
在MySQL,GROUP BY 是用于排序, 是以server 可能應用ORDER BY 優化分組。
8.2.1.16.1 Loose Index Scan
最有效的方式處理GROUP BY 是放一個索引用于直接檢索 grouping columns.
在這種通路方法裡,MySQL 使用一些索引類型的屬性 keys 是排序的(比如,BTREE).
這個屬性讓 lookup groups 使用在一個index 不比考慮所有的keys 在index,
滿足WHERE 子句的,這個通路方法被認為隻是一個 keys 在索引裡的一小部分,
是以被稱為是一個loose index scan.
當沒有一個WHERE 子句, 一個loose index scan 讀取讀取盡可能多的keys,
這個可能比全部的keys小的多。 如果WHERE 子句包含範圍謂詞
一個 loose index scan 查找每個group的第一個key滿足範圍條件的,
并在此讀取最少可能的鍵值數。 有下面的條件是可能的:
1.查詢通過單個表
GROUP BY 指令隻有列,形成一個 索引的最左字首,沒有其他列。
(如果, 代替GROUP BY,查詢有一個DISTINCT 子句,所有的單個屬性關聯列形成一個最左字首在索引上)
比如,一個表t1有一個index on(c1,c2,c3),loose index scan 是和使用的
如果查詢有GRAOUP BY c1,c2。它是不适用的如果查詢有GROUP BY c2,c3
(列不是最左字首) 或者 group by c1,c2,c4(c4不是在索引裡)
唯一的聚合函數使用在select 清單(如果有的話) 是MIN()和MAX(),
他們都指向一個相同的列, 列必須在是以裡, 列必須在GROUP BY 裡。
索引的其他部分比從GROUP BY關聯的在查詢部分必須是常數(也就是說,它們必須是等值關聯)
除了MIN() 或者MAX()函數:
對于索引中的列, 全部的列值必須被索引,不隻是一個字首。
比如,with c1 VARCHAR(20), INDEX (c1(10)), index不能用于loose index scan.
如果 loose index scan 是适用于查詢的,EXPLAIN 輸出适用index 用于group-by 在額外的列裡。
假設有一個index idx(c1,c2,c3) 在table t1(c1,c2,c3,c4).
loose index scan 通路方法可以用于下面查詢:
SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;
SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;
下面的查詢不能被執行使用這個快速查詢方法, 給出理由如下:
有聚合函數除了 MIN() or MAX():
SELECT c1, SUM(c2) FROM t1 GROUP BY c1;
列在GROUP BY 子句,不是形成一個最左邊淺醉的索引:
SELECT c1, c2 FROM t1 GROUP BY c2, c3;
檢視指向一個索引的部分,在GROUP BY 部分後面,沒有一個等于常值
SELECT c1, c3 FROM t1 GROUP BY c1, c2;
Were the query to include WHERE c3 = const, loose index scan could be used.