12.19 Aggregate (GROUP BY) Functions

12.19.1 Aggregate (GROUP BY) Function Descriptions
12.19.2 GROUP BY Modifiers
12.19.3 MySQL Handling of GROUP BYmysql

12.19.1 Aggregate (GROUP BY) Function Descriptions

本節描述對值集進行操做的組(聚合)函數。sql

Table 12.24 Aggregate (GROUP BY) Functions編程

Name Description
AVG() Return the average value of the argument
BIT_AND() Return bitwise AND
BIT_OR() Return bitwise OR
BIT_XOR() Return bitwise XOR
COUNT() Return a count of the number of rows returned
COUNT(DISTINCT) Return the count of a number of different values
GROUP_CONCAT() Return a concatenated string
MAX() Return the maximum value
MIN() Return the minimum value
STD() Return the population standard deviation
STDDEV() Return the population standard deviation
STDDEV_POP() Return the population standard deviation
STDDEV_SAMP() Return the sample standard deviation
SUM() Return the sum
VAR_POP() Return the population standard variance
VAR_SAMP() Return the sample variance
VARIANCE() Return the population standard variance

除非另有說明,不然組函數忽略空值。服務器

若是在不包含group BY子句的語句中使用GROUP函數,它至關於對全部行進行分組。有關更多信息,請參見第12.19.3節「MySQL Handling of GROUP BY」. 。併發

對於數值參數,方差和標準差函數返回一個DOUBLE值。SUM()和AVG()函數爲精確值參數(整數或十進制)返回十進制值,爲近似值參數(浮點或雙精度)返回雙精度值。函數

SUM()和AVG()聚合函數不適用於時間值。 (它們將值轉換爲數字,在第一個非數字字符後丟失全部內容。)要解決此問題,請轉換爲數字單位,執行聚合操做,而後轉換回時間值。 示例:性能

SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(time_col))) FROM tbl_name;
SELECT FROM_DAYS(SUM(TO_DAYS(date_col))) FROM tbl_name;

指望數字參數的SUM()AVG()等函數在必要時將參數強制轉換爲數字。 對於SET或ENUM值,強制轉換操做會致使使用基礎數值。測試

BIT_AND()BIT_OR()BIT_XOR()聚合函數執行位操做。 它們須要BIGINT(64位整數)參數並返回BIGINT值。 其餘類型的參數將轉換爲BIGINT並可能發生截斷。優化

  • AVG([DISTINCT] expr)code

    返回expr的平均值。 DISTINCT選項可用於返回expr的不一樣值的平均值。

    若是沒有匹配的行,AVG()返回NULL。

    mysql> SELECT student_name, AVG(test_score)
       FROM student
       GROUP BY student_name;
  • BIT_AND(expr)

    返回expr中全部位的按位AND。 計算以64位(BIGINT)精度執行。

    若是沒有匹配的行,BIT_AND()將返回一箇中間值(全部位都設置爲1)。

  • BIT_OR(expr)

    返回expr中全部位的按位OR。 計算以64位(BIGINT)精度執行。

    若是沒有匹配的行,BIT_OR()將返回一箇中間值(全部位都設置爲0)。

  • BIT_XOR(expr)

    返回expr中全部位的按位異或。 計算以64位(BIGINT)精度執行。

    若是沒有匹配的行,則BIT_XOR()返回中間值(全部位都設置爲0)。

  • COUNT(expr)

    返回SELECT語句檢索的行中expr的非空值的數量的計數。結果是一個BIGINT值。

    若是沒有匹配的行,COUNT()返回0。

    mysql> SELECT student.student_name,COUNT(*)
         FROM student,course
         WHERE student.student_id=course.student_id
         GROUP BY student_name;

    COUNT(*)有所不一樣,它返回檢索到的行數的計數,無論它們是否包含空值。

    對於InnoDB等事務存儲引擎,存儲精確的行數是有問題的。多個事務可能同時發生,每一個事務均可能影響計數。

    InnoDB不保留表中的內部行數,由於併發事務可能同時「看到」不一樣數量的行。 所以,SELECT COUNT(*)語句只計算當前事務可見的行。

    爲了處理SELECT COUNT(*)語句,InnoDB掃描表的索引,若是索引不徹底在緩衝池中,則須要一些時間。 爲了更快地計算,請建立一個計數器表,讓應用程序根據插入和刪除更新它。 可是,在數千個併發事務正在啓動對同一計數器表的更新的狀況下,此方法可能沒法很好地擴展。 若是大概的行數足夠,請使用SHOW TABLE STATUS。

    InnoDB以相同的方式處理SELECT COUNT(*)和SELECT COUNT(1)操做。 沒有性能差別。

    對於MyISAM表,COUNT(*)被優化爲若是SELECT從一個表中檢索,沒有檢索其餘列,而且沒有WHERE子句,則返回很是快。例如:

    mysql> SELECT COUNT(*) FROM student;

    這種優化僅適用於MyISAM表,由於存儲引擎存儲了精確的行數,而且能夠很是快地訪問。僅當第一列定義爲非空時,count(1)纔會進行相同的優化。

  • COUNT(DISTINCT expr,[expr...])

  • GROUP_CONCAT(expr)

  • MAX([DISTINCT] expr)

  • MIN([DISTINCT] expr)

  • STD(expr)

  • STDDEV(expr)

  • STDDEV_POP(expr)

  • STDDEV_SAMP(expr)

  • SUM([DISTINCT] expr)

  • VAR_POP(expr)

  • VAR_SAMP(expr)

  • VARIANCE(expr)

12.19.2 GROUP BY Modifie

GROUP BY子句容許一個WITH ROLLUP修飾符,該修飾符使彙總輸出包含表示更高級別(即超級彙總)彙總操做的額外行。所以,ROLLUP使您可以用一個查詢在多個分析級別回答問題。例如,彙總可用於爲OLAP(在線分析處理)運營提供支持。

假設一個 sales 表中有 year,country, product 和用於記錄銷售利潤的 profit 列:

CREATE TABLE sales
(
    year    INT,
    country VARCHAR(20),
    product VARCHAR(32),
    profit  INT
);

要總結每一年的表格內容,請使用簡單的GROUP BY,以下所示:

mysql> SELECT year, SUM(profit) AS profit
       FROM sales
       GROUP BY year;
+------+--------+
| year | profit |
+------+--------+
| 2000 |   4525 |
| 2001 |   3010 |
+------+--------+

輸出顯示每一年的總(總)利潤。 要肯定全部年份的總利潤總額,您必須本身添加單個值或運行其餘查詢。 或者您可使用ROLLUP,它使用單個查詢提供兩種級別的分析。 將WITH ROLLUP修飾符添加到GROUP BY子句會致使查詢生成另外一個(超級聚合)行,該行顯示全部年份值的總計:

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

year 列中的 NULL 值標識超級聚合行的總計數。

當存在多個GROUP BY列時,ROLLUP具備更復雜的效果。 在這種狀況下,每次除最後一個 grouping column以外的任何值發生更改時,查詢都會生成一個額外的超級聚合摘要行。

例如,若是沒有ROLLUP,則基於 year,country 和 product 的銷售表摘要可能以下所示,其中輸出僅在year/country/product 分析級別指示彙總值:

mysql> SELECT year, country, product, SUM(profit) AS profit
       FROM sales
       GROUP BY year, country, product;
+------+---------+------------+--------+
| year | country | product    | profit |
+------+---------+------------+--------+
| 2000 | Finland | Computer   |   1500 |
| 2000 | Finland | Phone      |    100 |
| 2000 | India   | Calculator |    150 |
| 2000 | India   | Computer   |   1200 |
| 2000 | USA     | Calculator |     75 |
| 2000 | USA     | Computer   |   1500 |
| 2001 | Finland | Phone      |     10 |
| 2001 | USA     | Calculator |     50 |
| 2001 | USA     | Computer   |   2700 |
| 2001 | USA     | TV         |    250 |
+------+---------+------------+--------+

添加ROLLUP後,查詢會生成幾個額外的行:

mysql> SELECT year, country, product, SUM(profit) AS profit
       FROM sales
       GROUP BY year, country, product WITH ROLLUP;
+------+---------+------------+--------+
| year | country | product    | profit |
+------+---------+------------+--------+
| 2000 | Finland | Computer   |   1500 |
| 2000 | Finland | Phone      |    100 |
| 2000 | Finland | NULL       |   1600 |
| 2000 | India   | Calculator |    150 |
| 2000 | India   | Computer   |   1200 |
| 2000 | India   | NULL       |   1350 |
| 2000 | USA     | Calculator |     75 |
| 2000 | USA     | Computer   |   1500 |
| 2000 | USA     | NULL       |   1575 |
| 2000 | NULL    | NULL       |   4525 |
| 2001 | Finland | Phone      |     10 |
| 2001 | Finland | NULL       |     10 |
| 2001 | USA     | Calculator |     50 |
| 2001 | USA     | Computer   |   2700 |
| 2001 | USA     | TV         |    250 |
| 2001 | USA     | NULL       |   3000 |
| 2001 | NULL    | NULL       |   3010 |
| NULL | NULL    | NULL       |   7535 |
+------+---------+------------+--------+

如今輸出包括四個層次的分析的摘要信息,而不只僅是一個層次

  • 在給定 year 和 country 的每一組產品行以後,將出現一個額外的超級彙總彙總行,顯示全部產品的總和。這些行將product列設置爲NULL。
  • 在給定 year 的每組行以後,將顯示一個額外的超級彙總摘要行,顯示全部 country 和 product 的總計。 這些行的country和products列設置爲NULL。
  • 最後,在全部其餘行以後,將出現一個額外的超級彙總彙總行,顯示全部年份、國家和產品的總金額。這一行將year、country和products列設置爲NULL。

將行發送到客戶端時,將生成每一個超級聚合行中的NULL指示符。 服務器查看GROUP BY子句中指定的列,這些列位於最左側的已更改值的子句以後。 對於結果集中名稱與其中任何名稱匹配的任何列,其值設置爲NULL。 (若是按列位置指定分組列,則服務器會按位置標識要設置爲NULL的列。)

因爲超級聚合行中的NULL值在查詢處理的後期階段放入結果集中,所以只能在選擇列表或HAVING子句中將它們做爲NULL值進行測試。 您沒法在鏈接條件或WHERE子句中將它們做爲NULL值進行測試,以肯定要選擇的行。 例如,您不能將WHERE product IS NULL添加到查詢中以從輸出中消除除超級聚合行以外的全部內容。

NULL值在客戶端顯示爲NULL,可使用任何MySQL客戶端編程接口進行測試。 可是,此時,您沒法區分NULL是表示常規分組值仍是超聚合值。

使用ROLLUP時的其餘注意事項

下面的討論列出了一些特定於ROLLUP的MySQL實現的行爲。

使用ROLLUP時,不能使用ORDER BY子句對結果進行排序。 換句話說,ROLLUP和ORDER BY在MySQL中是互斥的。 可是,您仍然能夠控制排序順序。 默認狀況下,MySQL中的GROUP BY隱式排序結果(在沒有ASC或DESC指示符的狀況下)。 可是,不推薦使用MySQL中的隱式GROUP BY排序。 要實現分組結果的特定排序順序:

  • 將明確的ASC和DESC關鍵字與GROUP BY列表中指定的列一塊兒使用,以指定各列的排序順序。 在這種狀況下,ROLLUP添加的超級聚合摘要行仍會顯示在計算它們的行以後,而無論排序順序如何。
  • 要解決使用ROLLUP 和 ORDER BY的限制,能夠將分組結果集生成爲派生表,並對其應用ORDER BY。例如

    mysql> SELECT * FROM
             (SELECT year, SUM(profit) AS profit
             FROM sales GROUP BY year WITH ROLLUP) AS dt
           ORDER BY year DESC;
    +------+--------+
    | year | profit |
    +------+--------+
    | 2001 |   3010 |
    | 2000 |   4525 |
    | NULL |   7535 |
    +------+--------+

    在這種狀況下,超級聚合摘要行按計算它們的行進行排序,它們的位置取決於排序順序(在開頭用於升序排序,在結尾用於降序排序)

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

mysql> SELECT year, country, product, SUM(profit) AS profit
       FROM sales
       GROUP BY year, country, product WITH ROLLUP
       LIMIT 5;
+------+---------+------------+--------+
| year | country | product    | profit |
+------+---------+------------+--------+
| 2000 | Finland | Computer   |   1500 |
| 2000 | Finland | Phone      |    100 |
| 2000 | Finland | NULL       |   1600 |
| 2000 | India   | Calculator |    150 |
| 2000 | India   | Computer   |   1200 |
+------+---------+------------+--------+

將LIMIT與ROLLUP一塊兒使用可能會產生更難解釋的結果,由於用於理解超聚合行的上下文較少。

MySQL擴展容許在選擇列表中命名未出如今GROUP BY列表中的列。 (有關非聚合列和GROUP BY的信息,請參見第12.19.3節「GROUP BY的MySQL處理」。)在這種狀況下,服務器能夠自由選擇摘要行中此非聚合列的任何值,這包括額外的 WITH ROLLUP添加的行。 例如,在如下查詢中,country是未出如今GROUP BY列表中的非聚合列,而且爲此列選擇的值是不肯定的:

mysql> SELECT year, country, SUM(profit) AS profit
       FROM sales
       GROUP BY year WITH ROLLUP;
+------+---------+--------+
| year | country | profit |
+------+---------+--------+
| 2000 | India   |   4525 |
| 2001 | USA     |   3010 |
| NULL | USA     |   7535 |
+------+---------+--------+

若是未啓用ONLY_FULL_GROUP_BY SQL模式,則容許此行爲。 若是啓用該模式,則服務器會將查詢拒絕爲非法,由於GROUP BY子句中未列出 country 。

12.19.3 Mysql對GROUP BY的處理

在標準SQL中,包含GROUP BY子句的查詢不能引用select列表中沒有在GROUP BY子句中命名的非聚合列。例如,這個查詢在標準SQL中是非法的,由於select列表中的非聚合name列未出如今GROUP BY中

SELECT o.custid, c.name, MAX(o.payment)
  FROM orders AS o, customers AS c
  WHERE o.custid = c.custid
  GROUP BY o.custid;

要使查詢合法,必須從選擇列表中省略name列,或在GROUP BY子句中指定name列。

MySQL擴展了GROUP BY的標準SQL使用,所以選擇列表能夠引用GROUP BY子句中未指定的非聚合列。 這意味着前面的查詢在MySQL中是合法的。 您能夠經過避免沒必要要的列排序和分組來使用此功能來得到更好的性能。 可是,當GROUP BY中未指定的每一個非聚合列中的全部值對於每一個組都相同時,這很是有用。 服務器能夠自由選擇每一個組中的任何值,所以除非它們相同,不然所選的值是不肯定的。 此外,添加ORDER BY子句不會影響每一個組中值的選擇。 結果集排序發生在選擇值以後,ORDER BY不會影響服務器選擇的每一個組中的值。

相似的MySQL擴展適用於HAVING子句。 在標準SQL中,查詢中HAVING子句不能引用未在GROUP BY子句中指定的非聚合列。 爲簡化計算,MySQL擴展容許引用此類列。 此擴展假定非組合列具備相同的分組值。 不然,結果是不肯定的。

要禁用MySQL GROUP BY擴展並啓用標準SQL行爲,請啓用ONLY_FULL_GROUP_BY SQL模式。 在這種狀況下,GROUP BY子句中未命名的列不能在選擇列表或HAVING子句中使用,除非包含在聚合函數中。

選擇列表擴展也適用於ORDER BY。 也就是說,您能夠在ORDER BY子句中引用未出如今GROUP BY子句中的非聚合列。 (可是,如前所述,ORDER BY不會影響從非聚合列中選擇哪些值;它只會在選擇它們後對它們進行排序。)若是啓用了ONLY_FULL_GROUP_BY SQL模式,則此擴展名不適用。

若是查詢具備聚合函數但沒有GROUP BY子句,啓用了ONLY_FULL_GROUP_BY的select列表、HAVING條件或ORDER BY列表中不能有未聚合的列:

mysql> SELECT name, MAX(age) FROM t;
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...)
with no GROUP columns is illegal if there is no GROUP BY clause

沒有GROUP BY,只有一個組,爲該組選擇哪一個 name 值是不肯定的。

標準SQL的另外一個MySQL擴展容許HAVING子句引用選擇列表中的別名表達式。啓用ONLY_FULL_GROUP_BY會阻止此操做。 例如,如下查詢返回在表順序中僅出現一次的名稱值; 不管是否啓用ONLY_FULL_GROUP_BY,都接受查詢:

SELECT name, COUNT(name) FROM orders
  GROUP BY name
  HAVING COUNT(name) = 1;

僅當禁用ONLY_FULL_GROUP_BY時,才接受如下查詢。

SELECT name, COUNT(name) AS c FROM orders
  GROUP BY name
  HAVING c = 1;

若是您想要遵循標準SQL,則只能在GROUP BY子句中使用列表達式。 做爲一種解決方法,請爲表達式使用別名:

SELECT id, FLOOR(value/100) AS val
  FROM tbl_name
  GROUP BY id, val;

MySQL容許GROUP BY子句中的非列表達式,所以別名是沒必要要的

SELECT id, FLOOR(value/100)
  FROM tbl_name
  GROUP BY id, FLOOR(value/100);
相關文章
相關標籤/搜索