MySQL 函数和运算符:汇总函数

来自Wikioe
跳到导航 跳到搜索


关于

汇总函数(or 集合函数???)对值集进行操作。它们通常与“GROUP BY”子句一起使用以将值分组为子集。

Name Description
AVG() 返回参数的平均值
BIT_AND() 按位返回 AND
BIT_OR() 按位返回 OR
BIT_XOR() 返回按位异或
COUNT() 返回计数返回的行数
COUNT(DISTINCT) 返回多个不同值的计数
GROUP_CONCAT() 返回串联的字符串
JSON_ARRAYAGG()(引入 5.7.22) 将结果集作为单个 JSON 数组返回
JSON_OBJECTAGG()(引入 5.7.22) 将结果集作为单个 JSON 对象返回
MAX() 返回最大值
MIN() 返回最小值
STD() 返回人口标准差
STDDEV() 返回人口标准差
STDDEV_POP() 返回人口标准差
STDDEV_SAMP() 返回 sample 标准偏差
SUM() 返回总和
VAR_POP() 返回总体标准方差
VAR_SAMP() 返回 samples 方差
VARIANCE() 返回总体标准方差

【说明:略】

GROUP BY 【WITH ROLLUP】

“GROUP BY”子句允许使用“WITH ROLLUP”修饰符,该修饰符使摘要输出包括代表更高级别(即超级汇总)摘要操作的额外行【???】。 ROLLUP 从而使您可以通过单个查询回答多个分析级别的问题。

例如,ROLLUP可用于提供对 OLAP(在线分析处理)操作的支持。

假设 salest 表具有 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,它通过一个查询提供两个分析级别。在GROUP BY子句中添加“WITH ROLLUP”修饰符会使查询产生另一行(超级汇总),该行显示所有年份值的总计:

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 的作用更为复杂。在这种情况下,每当除最后一个分组列之外的任何其他列中的值发生变化时,查询都会产生一个额外的超级汇总摘要行。

例如,如果没有 ROLLUP,则基于 year,country 和 product 的 salest 表的摘要可能看起来像这样,其中输出仅指示分析的年份/国家/产品级别的摘要值:

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 |
+------+---------+------------+--------+


  • 当超级汇总行中的NULL指示符发送到 Client 端时,它们就会产生。服务器查看在GROUP BY子句中命名的列,该列紧随其后的是更改了值的最左列。对于结果集中名称与任何名称匹配的任何列,其值均设置为NULL。 (如果您指定按列位置分组的列,则服务器会识别要按位置设置为NULL的列.)【???】
  • 由于超级聚合行中的 NULL 值是在查询处理的最后阶段放入结果集中的,因此只能在选择列表或“HAVING”子句中将它们作为 NULL 值进行测试。您不能在连接条件中或 WHERE 子句中将它们测试为NULL值来确定要选择的行。
    例如,您不能将“WHERE product IS NULL”添加到查询中以从输出中消除除超级聚合行以外的所有行。
  • NULL 的值在 Client 端确实显示为 NULL,并且可以使用任何 MySQLClient 端编程接口进行测试。但是,在这一点上,您无法区分 NULL 是表示常规分组值还是超汇总值。在 MySQL 8.0 中,可以使用“GROUPING()”函数测试区别。【???】

使用 ROLLUP 时的其他注意事项

  1. 使用 ROLLUP 时,也不能使用 ORDER BY 子句对结果进行排序。换句话说,ROLLUP 和 ORDER BY 在 MySQL 中是互斥的。但是,您仍然可以控制排序顺序。要解决防止将 ROLLUP 与 ORDER BY 结合使用并实现特定的分组结果排序 Sequences 的限制,请生成分组结果集作为派生表并将 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 |
    +------+--------+
    
    在这种情况下,超级汇总摘要行将根据其进行计算的行进行排序,并且它们的位置取决于排序 Sequences(在开始时是升序排序,在结束时是降序排序)。【???】
  2. LIMIT可用于限制返回给 Client 端的行数。 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 |
    +------+---------+------------+--------+
    
  3. MySQL 扩展允许在选择列表中命名不在 GROUP BY 列表中出现的列。在这种情况下,服务器可以从摘要行中的该未聚合的列中自由选择任何值,其中包括“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,因此服务器将查询拒绝为非法。启用“ONLY_FULL_GROUP_BY”后,您仍然可以通过对非确定值列使用“ANY_VALUE()”函数来执行查询:
      mysql> SELECT year, ANY_VALUE(country) AS country, SUM(profit) AS profit
             FROM sales
             GROUP BY year WITH ROLLUP;
      +------+---------+--------+
      | year | country | profit |
      +------+---------+--------+
      | 2000 | India   |   4525 |
      | 2001 | USA     |   3010 |
      | NULL | USA     |   7535 |
      +------+---------+--------+
      
      【???,还是没啥意义嘛?】

MySQL 对 GROUP BY 的处理

【接上一节最后,MySQL如何处理“选择列表,HAVING条件 或 ORDER BY列表引用未在GROUP BY子句中命名的未聚合列”的查询】


SQL-92 和更早版本不允许选择列表,HAVING条件或ORDER BY列表引用未在GROUP BY子句中命名的未聚合列的查询。

例如,此查询在标准 SQL-92 中是非法的,因为选择列表中未聚合的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;

为了使查询在 SQL-92 中合法,必须从选择列表中省略name列或在GROUP BY子句中命名。


SQL:1999 和更高版本允许在功能上依赖于GROUP BY列的每个非可选功能 T301 此类非聚合:如果name和custid之间存在这种关系,则查询合法。

例如,custid是customers的主键就是这种情况。


MySQL 5.7.5 及更高版本实现对功能依赖性的检测。如果启用了“ONLY_FULL_GROUP_BY” SQL 模式(默认情况下是默认设置),则 MySQL 拒绝选择列表,HAVING条件或ORDER BY列表引用未在GROUP BY子句中命名且在功能上不依赖于它们的未聚合列的查询。(在 5.7.5 之前,MySQL 不检测功能依赖关系,默认情况下未启用ONLY_FULL_GROUP_BY。)

启用ONLY_FULL_GROUP_BY SQL 模式时,MySQL 5.7.5 和更高版本还允许在GROUP BY子句中未命名的非聚合列,但前提是该列限于单个值,如以下示例所示:

mysql> CREATE TABLE mytable (
    ->    id INT UNSIGNED NOT NULL PRIMARY KEY,
    ->    a VARCHAR(10),
    ->    b INT
    -> );

mysql> INSERT INTO mytable
    -> VALUES (1, 'abc', 1000),
    ->        (2, 'abc', 2000),
    ->        (3, 'def', 4000);

mysql> SET SESSION sql_mode = sys.list_add(@@session.sql_mode, 'ONLY_FULL_GROUP_BY');

mysql> SELECT a, SUM(b) FROM mytable WHERE a = 'abc';
+------+--------+
| a    | SUM(b) |
+------+--------+
| abc  |   3000 |
+------+--------+

使用ONLY_FULL_GROUP_BY时,也可能在SELECT列 table 中有多个非聚合列。在这种情况下,每个这样的列都必须限制为一个值,并且所有这样的限制条件必须由逻辑“AND”联接,如下所示:

mysql> DROP TABLE IF EXISTS mytable;

mysql> CREATE TABLE mytable (
    ->    id INT UNSIGNED NOT NULL PRIMARY KEY,
    ->    a VARCHAR(10),
    ->    b VARCHAR(10),
    ->    c INT
    -> );

mysql> INSERT INTO mytable
    -> VALUES (1, 'abc', 'qrs', 1000),
    ->        (2, 'abc', 'tuv', 2000),
    ->        (3, 'def', 'qrs', 4000),
    ->        (4, 'def', 'tuv', 8000),
    ->        (5, 'abc', 'qrs', 16000),
    ->        (6, 'def', 'tuv', 32000);

mysql> SELECT @@session.sql_mode;
+---------------------------------------------------------------+
| @@session.sql_mode                                            |
+---------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------------------------------------------------------+

mysql> SELECT a, b, SUM(c) FROM mytable
    ->     WHERE a = 'abc' AND b = 'qrs';
+------+------+--------+
| a    | b    | SUM(c) |
+------+------+--------+
| abc  | qrs  |  17000 |
+------+------+--------+

如果禁用了“ONLY_FULL_GROUP_BY”,则对标准 SQL 使用GROUP BY的 MySQL 扩展允许选择列表,HAVING条件或ORDER BY列表引用未聚合的列,即使这些列在功能上不依赖于GROUP BY列也是如此。这使 MySQL 接受前面的查询。在这种情况下,服务器可以从每个组中自由选择任何值,因此,除非它们相同,否则选择的值是不确定的,这可能不是您想要的。

此外,通过添加ORDER BY子句不能影响每个组中值的选择。选择值之后,将进行结果集排序,并且ORDER BY不会影响服务器在每个组中选择哪个值。

  • 禁用“ONLY_FULL_GROUP_BY”一般是有用的,因为您知道由于数据的某些属性,每个未聚合的列中在GROUP BY中未命名的所有值对于每个组都是相同的。
  • 通过使用“ANY_VALUE()”引用未聚合的列,您可以在不禁用“ONLY_FULL_GROUP_BY”的情况下实现相同的效果。





关于功能依赖性

下面的讨论演示了功能依赖性,不存在功能依赖性时 MySQL 产生的错误消息,以及在没有功能依赖性的情况下使 MySQL 接受查询的方法。
【。。。。。略。。。。。】

检测函数依赖性

【。。。。。略。。。。。】