MySql 資料庫group by 的用法,order by 巢狀使用。優化問題
不多說,直接看例子。自己動手試試
mysql> select * from tb_clothes; +----+--------+-------+-------+------------+---------+ | id | name | price | total | date | address | +----+--------+-------+-------+------------+---------+ | 1 | 上衣 | 45 | 89 | 2010-01-09 | 河南 | | 2 | 手套 | 7 | 45 | 2010-02-09 | 山東 | | 3 | 上衣 | 56 | 34 | 2010-01-09 | 河南 | | 4 | 手套 | 7 | 12 | 2010-02-09 | 山東 | | 5 | 褲子 | 34 | 56 | 2010-04-12 | 吉林 | | 6 | 毛衣 | 25 | 22 | 2010-08-09 | 吉林 | | 7 | 籃球鞋 | 125 | 23 | 2010-09-12 | 河南 | | 8 | 毛衣 | 25 | 67 | 2010-08-09 | 湖北 | | 9 | 帽子 | 8 | 14 | 2010-10-09 | 吉林 | | 10 | 帽子 | 8 | 54 | 2010-10-09 | 陝西 | +----+--------+-------+-------+------------+---------+ 10 rows in set mysql> select * from tb_clothes group by address; +----+------+-------+-------+------------+---------+ | id | name | price | total | date | address | +----+------+-------+-------+------------+---------+ | 5 | 褲子 | 34 | 56 | 2010-04-12 | 吉林 | | 2 | 手套 | 7 | 45 | 2010-02-09 | 山東 | | 1 | 上衣 | 45 | 89 | 2010-01-09 | 河南 | | 8 | 毛衣 | 25 | 67 | 2010-08-09 | 湖北 | | 10 | 帽子 | 8 | 54 | 2010-10-09 | 陝西 | +----+------+-------+-------+------------+---------+ 5 rows in set mysql> select price from tb_clothes group by address; +-------+ | price | +-------+ | 34 | | 7 | | 45 | | 25 | | 8 | +-------+ 5 rows in set mysql> select sum(price) from tb_clothes group by address; +------------+ | sum(price) | +------------+ | 67 | | 14 | | 226 | | 25 | | 8 | +------------+ 5 rows in set
mysql> select sum(price) as SumPrice,address from tb_clothes group by address;
+----------+---------+
| SumPrice | address |
+----------+---------+
| 67 | 吉林 |
| 14 | 山東 |
| 226 | 河南 |
| 25 | 湖北 |
| 8 | 陝西 |
+----------+---------+
5 rows in set
與排序的巢狀使用:
mysql> select sum(price) as SumPrice,address from tb_clothes group by address order by SumPrice ; +----------+---------+ | SumPrice | address | +----------+---------+ | 8 | 陝西 | | 14 | 山東 | | 25 | 湖北 | | 67 | 吉林 | | 226 | 河南 | +----------+---------+ 5 rows in set
優化問題:
複製高手的
滿足GROUP BY子句的最一般的方法是掃描整個表並建立一個新的臨時表,表中每個組的所有行應為連續的,然後使用該臨時表來找到組並應用累積函式(如果有)。在某些情況中,MySQL能夠做得更好,通過索引訪問而不用建立臨時表。
為GROUP BY使用索引的最重要的前提條件是 所有GROUP BY列引用同一索引的屬性,並且索引按順序儲存其關鍵字(例如,這是B-樹索引,而不是HASH索引)。是否用索引訪問來代替臨時表的使用還取決於在查詢中使用了哪部分索引、為該部分指定的條件,以及選擇的累積函式。
有兩種方法通過索引訪問執行GROUP BY查詢,如下面的章節所描述。在第1個方法中,組合操作結合所有範圍判斷式使用(如果有)。第2個方法首先執行範圍掃描,然後組合結果元組。
7.2.13.1. 鬆散索引掃描
使用索引時最有效的途徑是直接搜尋組域。通過該訪問方法,MySQL使用某些關鍵字排序的索引型別(例如,B-樹)的屬性。該屬性允許使用 索引中的查詢組而不需要考慮滿足所有WHERE條件的索引中的所有關鍵字。既然該訪問方法只考慮索引中的關鍵字的一小部分,它被稱為鬆散索引掃描。如果沒有WHERE子句, 鬆散索引掃描讀取的關鍵字數量與組數量一樣多,可以比所有關鍵字數小得多。如果WHERE子句包含範圍判斷式(關於range聯接型別的討論參見7.2.1節,“EXPLAIN語法(獲取關於SELECT的資訊)”), 鬆散索引掃描查詢滿足範圍條件的每個組的第1個關鍵字,並且再次讀取儘可能最少數量的關鍵字。在下面的條件下是可以的:
·查詢針對一個單表。
·GROUP BY包括索引的第1個連續部分(如果對於GROUP BY,查詢有一個DISTINCT子句,則所有顯式屬性指向索引開頭)。
·只使用累積函式(如果有)MIN()和MAX(),並且它們均指向相同的列。
·索引的任何其它部分(除了那些來自查詢中引用的GROUP BY)必須為常數(也就是說,必須按常量數量來引用它們),但MIN()或MAX()函式的引數例外。
此類查詢的EXPLAIN輸出顯示Extra列的Using indexforgroup-by。
下面的查詢提供該類的幾個例子,假定表t1(c1,c2,c3,c4)有一個索引idx(c1,c2,c3):
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;
由於上述原因,不能用該快速選擇方法執行下面的查詢:
1.除了MIN()或MAX(),還有其它累積函式,例如:
SELECT c1, SUM(c2) FROM t1 GROUP BY c1;
2.GROUP BY子句中的域不引用索引開頭,如下所示:
SELECT c1,c2 FROM t1 GROUP BY c2, c3;
3.查詢引用了GROUP BY部分後面的關鍵字的一部分,並且沒有等於常量的等式,例如:
SELECT c1,c3 FROM t1 GROUP BY c1, c2;
7.2.13.2. 緊湊索引掃描
緊湊式索引掃描可以為索引掃描或一個範圍索引掃描,取決於查詢條件。
如果不滿足鬆散索引掃描條件,GROUP BY查詢仍然可以不用建立臨時表。如果WHERE子句中有範圍條件,該方法只讀取滿足這些條件的關鍵字。否則,進行索引掃描。該方法讀取由WHERE子句定義的每個範圍的所有關鍵字,或沒有範圍條件式掃描整個索引,我們將它定義為緊湊式索引掃描。請注意對於緊湊式索引掃描,只有找到了滿足範圍條件的所有關鍵字後才進行組合操作。
要想讓該方法工作,對於引用GROUP BY關鍵字元素的前面、中間關鍵字元素的查詢中的所有列,有一個常量等式條件即足夠了。等式條件中的常量填充了搜尋關鍵字中的“差距”,可以形成完整的索引字首。這些索引字首可以用於索引查詢。如果需要排序GROUP BY結果,並且能夠形成索引字首的搜尋關鍵字,MySQL還可以避免額外的排序操作,因為使用有順序的索引的字首進行搜尋已經按順序檢索到了所有關鍵字。
上述的第一種方法不適合下面的查詢,但第2種索引訪問方法可以工作(假定我們已經提及了表t1的索引idx):
·GROUP BY中有一個差距,但已經由條件c2 = 'a'覆蓋。
SELECT c1,c2,c3 FROM t1 WHERE c2 = 'a' GROUP BY c1,c3;
·GROUP BY不以關鍵字的第1個元素開始,但是有一個條件提供該元素的常量:
SELECT c1,c2,c3 FROM t1 WHERE c1 = 'a' GROUP BY c2,c3;
7.2.14. MySQL如何優化LIMIT
在一些情況中,當你使用LIMIT row_count而不使用HAVING時,MySQL將以不同方式處理查詢。
·如果你用LIMIT只選擇一些行,當MySQL選擇做完整的表掃描時,它將在一些情況下使用索引。
·如果你使用LIMIT row_count與ORDER BY,MySQL一旦找到了排序結果的第一個row_count行,將結束排序而不是排序整個表。如果使用索引,將很快。如果必須進行檔案排序(filesort),必須選擇所有匹配查詢沒有LIMIT子句的行,並且在確定已經找到第1個row_count行前,必須對它們的大部分進行排序。在任何一種情況下,一旦找到了行,則不需要再排序結果的其它部分,並且MySQL不再進行排序。
·當結合LIMIT row_count和DISTINCT時,MySQL一旦找到row_count個唯一的行,它將停止。
·在一些情況下,GROUP BY能通過順序讀取鍵(或在鍵上做排序)來解決,然後計算摘要直到關鍵字的值改變。在這種情況下,LIMIT row_count將不計算任何不必要的GROUP BY值。
·只要MySQL已經發送了需要的行數到客戶,它將放棄查詢,除非你正使用SQL_CALC_FOUND_ROWS。
·LIMIT 0將總是快速返回一個空集合。這對檢查查詢的有效性是有用的。當使用MySQL API時,它也可以用來得到結果列的列型別。(該技巧在MySQL Monitor中不工作,只顯示Empty set;應使用SHOW COLUMNS或DESCRIBE)。
·當伺服器使用臨時表來進行查詢時,使用LIMIT row_count子句來計算需要多少空間。
7.2.15. 如何避免表掃描
EXPLAIN的輸出顯示了當MySQL使用表掃描來解決查詢時使用的所有型別列。這通常在如下條件下發生:
·表很小,掃描表比查詢關鍵字速度快。這對於少於10行並且行較短的表比較普遍。
·在ON或WHERE子句中沒有適用的索引列的約束。
·正用常量值比較索引列,並且MySQL已經計算到(基於索引樹)常數覆蓋了表的很大部分並且表掃描將會比較快。參見7.2.4節,“MySQL怎樣優化WHERE子句”。
·你正通過另一個列使用一個低的集的勢的關鍵字(許多行匹配關鍵字)。在這種情況下,MySQL假設通過使用關鍵字它可能會進行許多關鍵字查詢,表掃描將會更快。
對於小表,表掃描通常合適。對於大表,嘗試下面的技巧以避免優化器錯選了表掃描:
·使用ANALYZE TABLE tbl_name為掃描的表更新關鍵字分佈。參見13.5.2.1節,“ANALYZE TABLE語法”。
·對掃描的表使用FORCEINDEX告知MySQL,相對於使用給定的索引表掃描將非常耗時。參見13.2.7節,“SELECT語法”。
·SELECT * FROM t1, t2 FORCE INDEX (index_for_column)
· WHERE t1.col_name=t2.col_name;
·用--max-seeks-for-key=1000選項啟動mysqld或使用SET max_seeks_for_key=1000告知優化器假設關鍵字掃描不會超過1,000次關鍵字搜尋。