2010-03-17 31 views
7

我從MySQL參考手冊中讀到,發現它可以使用索引時,它只是執行索引掃描,其他它將創建tmp表並執行像filesort這樣的操作。 而且我還從其他文章中讀到,「分組依據」結果將默認按列分組,如果添加了「by order by null」子句,則不會執行文件分類。 區別可以從「explain ...」子句中找到。 所以我的問題是:什麼是區別「group by」從句與「order by null」並沒有? 我嘗試使用剖析,看看MySQL在後臺做的,只能看到結果,如:MySQL如何實現「group by」?

result for group clause without order by null: 
|preparing      | 0.000016 | 
| Creating tmp table    | 0.000048 | 
| executing      | 0.000009 | 
| Copying to tmp table   | 0.000109 | 
**| Sorting result     | 0.000023 |** 
| Sending data     | 0.000027 | 

result for clause with "order by null": 
preparing      | 0.000016 | 
| Creating tmp table    | 0.000052 | 
| executing      | 0.000009 | 
| Copying to tmp table   | 0.000114 | 
| Sending data     | 0.000028 | 

所以我猜的MySQL辦時,「秩序由空」補充說,它不使用文件排序算法,也許當它創建tmp表時,它也使用索引,然後使用索引通過操作來完成分組,當完成時,它只是從錶行中讀取結果並且不對結果進行排序。

但我最初的意見是,MySQL可以使用quicksort對項目進行排序,然後按group by排序,因此結果也會被排序。

任何意見讚賞,謝謝。

回答

-1

分組由一些列分組記錄。例如,您有列「類」,您可以按此列進行分組,以便根據此列值獲取分組記錄。

1
mysql> select max(post_date),post_author from wp_posts 
-> where id > 10 and id < 1000 
-> group by post_author; 
+———————+————-+ 
| max(post_date) | post_author | 
+———————+————-+ 
| 2009-07-03 12:58:39 | 1 | 
+———————+————-+ 
1 row in set (0.01 sec) 

mysql> show profiles; 
+———-+————+————————+ 
| Query_ID | Duration | Query | 
+———-+————+————————+ 
| 1 | 0.00013200 | SELECT DATABASE() | 
| 2 | 0.00030900 | show databases | 
| 3 | 0.00030400 | show tables | 
| 4 | 0.01180000 | select max(post_date),post_author from wp_posts where id > 10 and id < 1000 group by post_author |4 rows in set (0.00 sec) 

mysql> show profile cpu,block io for query 4; 
+———————-+———-+———-+————+————–+—————+ 
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | 
+———————-+———-+———-+————+————–+—————+ 
| starting | 0.000085 | 0.000000 | 0.000000 | 0 | 0 | 
| Opening tables | 0.000010 | 0.000000 | 0.000000 | 0 | 0 | 
| System lock | 0.000005 | 0.000000 | 0.000000 | 0 | 0 | 
| Table lock | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | 
| init | 0.000029 | 0.000000 | 0.000000 | 0 | 0 | 
| optimizing | 0.000014 | 0.000000 | 0.000000 | 0 | 0 | 
| statistics | 0.000062 | 0.000000 | 0.000000 | 0 | 0 | 
| preparing | 0.000016 | 0.000000 | 0.000000 | 0 | 0 | 
| Creating tmp table | 0.000035 | 0.000000 | 0.000000 | 0 | 0 | 
| executing | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| Copying to tmp table | 0.011386 | 0.004999 | 0.006999 | 0 | 0 | 
| Sorting result | 0.000044 | 0.000000 | 0.000000 | 0 | 0 | 
| Sending data | 0.000036 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| removing tmp table | 0.000012 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| query end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| freeing items | 0.000013 | 0.000000 | 0.000000 | 0 | 0 | 
| closing tables | 0.000018 | 0.000000 | 0.000000 | 0 | 0 | 
| logging slow query | 0.000003 | 0.000000 | 0.000000 | 0 | 0 | 
| cleaning up | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
+———————-+———-+———-+————+————–+—————+ 
22 rows in set (0.00 sec) 

mysql> 
mysql> 
mysql> select max(post_date),post_author from wp_posts 
-> where id > 10 and id < 1000 
-> group by post_author order by null; 
+———————+————-+ 
| max(post_date) | post_author | 
+———————+————-+ 
| 2009-07-03 12:58:39 | 1 | 
+———————+————-+ 
1 row in set (0.01 sec) 

mysql> show profiles; 
+———-+————+—————–+ 
| Query_ID | Duration | Query 
+———-+————+—————–+ 
|1 | 0.00013200 | SELECT DATABASE() 
|2 | 0.00030900 | show databases 
|3 | 0.00030400 | show tables 
|4 | 0.01180000 | select max(post_date),post_author from wp_posts where id > 10 and id < 1000 group by post_author 
|5 | 0.01177700 | select max(post_date),post_author from wp_posts where id > 10 and id < 1000 group by post_author order by null 
5 rows in set (0.00 sec) 
mysql> show profile cpu,block io for query 5; 
+———————-+———-+———-+————+————–+—————+ 
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | 
+———————-+———-+———-+————+————–+—————+ 
| starting | 0.000097 | 0.000000 | 0.000000 | 0 | 0 | 
| Opening tables | 0.000013 | 0.000000 | 0.000000 | 0 | 0 | 
| System lock | 0.000006 | 0.000000 | 0.000000 | 0 | 0 | 
| Table lock | 0.000008 | 0.000000 | 0.000000 | 0 | 0 | 
| init | 0.000032 | 0.000000 | 0.000000 | 0 | 0 | 
| optimizing | 0.000012 | 0.000000 | 0.000000 | 0 | 0 | 
| statistics | 0.000065 | 0.000000 | 0.000000 | 0 | 0 | 
| preparing | 0.000017 | 0.000000 | 0.000000 | 0 | 0 | 
| Creating tmp table | 0.000040 | 0.000000 | 0.000000 | 0 | 0 | 
| executing | 0.000003 | 0.000000 | 0.000000 | 0 | 0 | 
| Copying to tmp table | 0.011369 | 0.005999 | 0.004999 | 0 | 0 | 
| Sending data | 0.000040 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| removing tmp table | 0.000031 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000005 | 0.000000 | 0.000000 | 0 | 0 | 
| end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| query end | 0.000004 | 0.000000 | 0.000000 | 0 | 0 | 
| freeing items | 0.000012 | 0.000000 | 0.000000 | 0 | 0 | 
| closing tables | 0.000009 | 0.000000 | 0.000000 | 0 | 0 | 
| logging slow query | 0.000003 | 0.000000 | 0.000000 | 0 | 0 | 
| cleaning up | 0.000003 | 0.000000 | 0.000000 | 0 | 0 | 
+———————-+———-+———-+————+————–+—————+ 
21 rows in set (0.00 sec) 

從這裏我們可以看到,第二部分沒有「排序結果」步驟,所以對性能有一點影響。

+0

好吧,我已經在上面提到過了。 我真正想知道的是mysql執行「group by」操作的方式。 – user188916 2010-03-18 03:16:38

1

GROUP BY子句允許使用WITH ROLLUP修飾符,該修飾符會將多餘的行添加到摘要輸出中。這些行代表更高級別(或超級聚合)摘要操作。因此,ROLLUP允許您使用單個查詢在多個分析級別回答問題。例如,它可用於爲OLAP(聯機分析處理)操作提供支持。

假設名爲銷售表中有年份,國家,產品和利潤記錄銷售利潤率列:

CREATE TABLE銷售 ( 年INT NOT NULL, 國家VARCHAR(20)NOT NULL, product VARCHAR(32)NOT NULL, profit INT );

表的內容,每年可歸納用一個簡單的GROUP BY這樣的:

的mysql> SELECT年,SUM(利潤)銷售GROUP BY年; + ------ + ------------- + |年| | SUM(利潤)| + ------ + ------------- + | 2000 | 4525 | | 2001 | 3010 | + ------ + ------------- +

此輸出顯示每年的總利潤,但是如果您還想確定總計的總利潤總和年,您必須自己添加個別值或運行其他查詢。

或者您可以使用ROLLUP,它通過單個查詢提供兩種級別的分析。將WITH ROLLUP修飾符添加到GROUP BY子句會導致查詢生成另一行,顯示所有年份值的總和:

mysql> SELECT year,SUM(profit)FROM sales GROUP BY year WITH ROLLUP; + ------ + ------------- + |年| | SUM(利潤)| + ------ + ------------- + | 2000 | 4525 | | 2001 | 3010 | | NULL | 7535 | + ------ + ------------- +

總的超級聚合線由年份列中的NULL值標識。

當有多個GROUP BY列時,ROLLUP會有更復雜的效果。在這種情況下,每當除了最後一個分組列以外的任何一個「中斷」(更改值)時,查詢都會生成一個額外的超級聚合摘要行。

例如,如果沒有ROLLUP的基礎上,今年,國家和產品的銷售表的總結可能是這樣的:

的mysql> SELECT年份,國家,產品,SUM(利潤) - > FROM銷售 - > GROUP BY年份,國家,產品; + ------ + --------- + ------------ + ------------- + |年| |國家|產品| SUM(利潤)| + ------ + --------- + ------------ + ------------- + | 2000 |芬蘭|計算機| 1500 | | 2000 |芬蘭|電話| 100 | | 2000 |印度|計算器| 150 | | 2000 |印度|計算機| 1200 | | 2000 | USA |計算器| 75 | | 2000 | USA |計算機| 1500 | | 2001 |芬蘭|電話| 10 | | 2001 | USA |計算器| 50 | | 2001 | USA |計算機| 2700 | | 2001 | USA |電視| 250 | + ------ + --------- + ------------ + ------------- +

輸出結果僅顯示年份/國家/產品分析級別的彙總值。當添加彙總是,查詢產生幾種額外行:

的mysql> SELECT年份,國家,產品,SUM(利潤) - >銷售 - > GROUP BY年,國家,ROLLUP產品; + ------ + --------- + ------------ + ------------- + |年| |國家|產品| SUM(利潤)| + ------ + --------- + ------------ + ------------- + | 2000 |芬蘭|計算機| 1500 | | 2000 |芬蘭|電話| 100 | | 2000 |芬蘭| NULL | 1600 | | 2000 |印度|計算器| 150 | | 2000 |印度|計算機| 1200 | | 2000 |印度| NULL | 1350 | | 2000 | USA |計算器| 75 | | 2000 | USA |計算機| 1500 | | 2000 | USA | NULL | 1575 | | 2000 | NULL | NULL | 4525 | | 2001 |芬蘭|電話| 10 | | 2001 |芬蘭| NULL | 10 | | 2001 | USA |計算器| 50 | | 2001 | USA |計算機| 2700 | | 2001 | USA |電視| 250 | | 2001 | USA | NULL | 3000 | | 2001 | NULL | NULL | 3010 | | NULL | NULL | NULL | 7535 | + ------ + --------- + ------------ + ------------- +

對於此查詢,添加ROLLUP會導致輸出包含四個分析級別的摘要信息,而不僅僅是一個。下面是如何解釋ROLLUP輸出:

* 

    Following each set of product rows for a given year and country, an extra summary row is produced showing the total for all products. These rows have the product column set to NULL. 
* 

    Following each set of rows for a given year, an extra summary row is produced showing the total for all countries and products. These rows have the country and products columns set to NULL. 
* 

    Finally, following all other rows, an extra summary row is produced showing the grand total for all years, countries, and products. This row has the year, country, and products columns set to NULL. 

其他注意事項使用ROLLUP

下列項目列表一些具體到MySQL執行ROLLUP的行爲:

當您使用ROLLUP,你不能還使用ORDER BY子句對結果進行排序。換句話說,ROLLUP和ORDER BY是互斥的。但是,您仍然可以對排序順序進行一些控制。 MySQL中的GROUP BY對結果進行排序,並且可以使用明確的ASC和DESC關鍵字以及GROUP BY列表中指定的列來指定單個列的排序順序。 (無論排序順序如何,ROLLUP添加的更高級彙總行仍會出現在計算它們的行後面)。

LIMIT可用於限制返回給客戶端的行數。 LIMIT在ROLLUP之後應用,因此該限制適用於ROLLUP添加的額外行。例如:

的mysql> SELECT年份,國家,產品,SUM(利潤) - >銷售 - > GROUP BY年,國家,ROLLUP產品 - > LIMIT 5; + ------ + --------- + ------------ + ------------- + |年| |國家|產品| SUM(利潤)| + ------ + --------- + ------------ + ------------- + | 2000 |芬蘭|計算機| 1500 | | 2000 |芬蘭|電話| 100 | | 2000 |芬蘭| NULL | 1600 | | 2000 |印度|計算器| 150 | | 2000 |印度|計算機| 1200 | + ------ + --------- + ------------ + ------------- +

將LIMIT與ROLLUP一起使用可能會產生更難解釋的結果,因爲您理解超集合行的上下文較少。

當將行發送到客戶端時,會生成每個超集合行中的NULL指示符。服務器查看最​​後一個已更改值的GROUP BY子句中指定的列。對於結果集中具有與任何這些名稱進行詞法匹配的名稱的任何列,其值都設置爲NULL。 (如果按列編號指定分組列,則服務器通過編號標識要設置爲NULL的列。)

由於超集合行中的NULL值在查詢的後期階段放入結果集中處理,您不能在查詢本身內將它們作爲NULL值進行測試。例如,您不能將HAVING產品IS NULL添加到查詢中,以從輸出中除去超集合行以外的所有行。

另一方面,NULL值在客戶端顯示爲NULL,可以使用任何MySQL客戶端編程接口進行測試。