12章 汇总数据
聚集函数(aggregate function) 运行在行组上,计算和返回单个值的函数
1. 确定表中行数(或者满足某个条件或包含某个特定值的行数)。
2. 获得表中行组的和。
3. 找出表列(或所有行或某些特定的行)的最大值、最小值和平均值。
SQL聚集函数
函 数 说 明
AVG() 返回某列的平均值
COUNT() 返回某列的行数
MAX() 返回某列的最大值
MIN() 返回某列的最小值
SUM() 返回某列值之和
举例:
AVG()函数
mysql> select avg(prod_price) as avg_price from products;
mysql> select avg(prod_price) as avg_price from products where vend_id = 1003; //指定条件
+-----------+
| avg_price |
+-----------+
| 13.212857 |
+-----------+
COUNT()函数
检索客户总数:
mysql> select count(*) as num_cust from customers;
+----------+
| num_cust |
+----------+
| 5 |
+----------+
COUNT()函数有两种使用方式:
1. 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
2. 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
mysql> select count(cust_email) as num_cust from customers; //只对具有电子邮件地址的客户计数
MAX()函数
返回指定列中的最大值, 需要指定列名, MAX()函数忽略列值为NULL的行。
mysql> select max(prod_price) as max_pricce from products;
MIN()函数
返回指定列的最小值。与MAX()一样,MIN()要求指定列名,忽略列值为NULL的行
SUM()函数
mysql> select quantity from orderitems where order_num= 20005;
+----------+
| quantity |
+----------+
| 10 |
| 3 |
| 5 |
| 1 |
+----------+
mysql> select sum(quantity) as items_ordered from orderitems where order_num= 20005;
+---------------+
| items_ordered |
+---------------+
| 19 |
+---------------+
聚集不同值
使用DISTINCT
mysql> select avg(distinct prod_price) as avg_price from products where vend_id = 1003;
+-----------+
| avg_price |
+-----------+
| 15.998000 |
+-----------+
使用了DISTINCT后,此例子中的avg_price比较高,因为有多个物品具有相同的较低价格。排除它们提升了平均价格。
组合聚集函数
mysql> select count(*) as num_items, min(prod_price) as price_min,max(prod_price) as price_max, avg(prod_price) as prive_avg from products;
+-----------+-----------+-----------+-----------+
| num_items | price_min | price_max | prive_avg |
+-----------+-----------+-----------+-----------+
| 14 | 2.50 | 55.00 | 16.133571 |
+-----------+-----------+-----------+-----------+
13章 分组数据
group by 分组允许把数据分为多个逻辑组,以便能对每个组进行聚集计算.
创建分组
mysql> select vend_id, count(*) as num_prods from products;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| 1001 | 14 |
+---------+-----------+
mysql> select vend_id, count(*) as num_prods from products group by vend_id;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| 1001 | 3 |
| 1002 | 2 |
| 1003 | 7 |
| 1005 | 2 |
+---------+-----------+
在具体使用GROUP BY子句前,需要知道一些重要的规定:
1. GROUP BY子句可以包含任意数目的列。这使得能对分组进行嵌套,为数据分组提供更细致的控制。
2. 如果在GROUP BY子句中嵌套了分组,数据将在最后规定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算
(所以不能从个别的列取回数据)。
3. GROUP BY子句中列出的每个列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在
GROUP BY子句中指定相同的表达式。不能使用别名。
4. 除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
5. 如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。
6. GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
过滤分组
HAVING子句
1. HAVING非常类似于WHERE, 所有类型的WHERE子句都可以用HAVING来替代。唯一的差别是WHERE过滤行,而HAVING过滤分组。
2. HAVING支持所有WHERE操作符
mysql> select cust_id, count(*) as orders from orders group by cust_id having count(*)>=2;
+---------+--------+
| cust_id | orders |
+---------+--------+
| 10001 | 2 |
+---------+--------+
这里WHERE子句不起作用,因为过滤是基于分组聚集值而不是特定行值的
mysql> select vend_id,count(*) as num_prods from products where prod_price >=10 group by vend_id
--> having count(*) >=2;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| 1003 | 4 |
| 1005 | 2 |
+---------+-----------+
分组和排序
ORDER BY与GROUP BY
ORDER BY GROUP BY
+--------------------------+--------------------------------------------------
| 排序产生的输出 | 分组行。但输出可能不是分组的顺序
+--------------------------+--------------------------------------------------
| 任意列都可以使用(甚至 | 只可能使用选择列或表达式列,而且必须使用每个选择
| 非选择的列也可以使用) | 列表达式
+--------------------------+--------------------------------------------------
| 不一定需要 | 如果与聚集函数一起使用列(或表达式),则必须使用
+--------------------------+--------------------------------------------------
mysql> select order_num,sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum(quantity*item_price)>=50;
+-----------+------------+
| order_num | ordertotal |
+-----------+------------+
| 20005 | 149.87 |
| 20006 | 55.00 |
| 20007 | 1000.00 |
| 20008 | 125.00 |
+-----------+------------+
mysql> select order_num, sum(quantity*item_price) as ordertotal from orderitems group by order_num having sum
-->(quantity*item_price)>=50 order by ordertotal;
+-----------+------------+
| order_num | ordertotal |
+-----------+------------+
| 20006 | 55.00 |
| 20008 | 125.00 |
| 20005 | 149.87 |
| 20007 | 1000.00 |
+-----------+------------+
SELECT子句顺序
SELECT子句及其顺序: 以下为必须遵循的顺序。
子 句 说 明 是否必须使用
SELECT 要返回的列或表达式 是
FROM 从中检索数据的表 仅在从表选择数据时使用
WHERE 行级过滤 否
GROUP BY 分组说明 仅在按组计算聚集时使用
HAVING 组级过滤 否
ORDER BY 输出排序顺序 否
LIMIT 要检索的行数 否
14章 使用子查询
子查询(subquery),即嵌套在其他查询中的查询。
mysql> select cust_id from orders where order_num in (select order_num from orderitems where prod_id='TNT2');
+---------+
| cust_id |
+---------+
| 10001 |
| 10004 |
+---------+
嵌套查询顺序:子查询总是从内向外处理
15章 联结表
一些概念:
1. 联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结
2. 外键(foreign key): 外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。
3. 如果数据存储在多个表中,怎样用单条SELECT语句检索出数据?答案是使用联结
4. SQL最强大的功能之一就是能在数据检索查询的执行中联结(join)表。
举例:
mysql> select vend_name, prod_name,prod_price from vendors,products
--> where vendors.vend_id = products.vend_id order by vend_name,prod_name;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Detonator | 13.00 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Anvils R us | .5 ton anvil | 5.99 |
| Anvils R us | 1 ton anvil | 9.99 |
| Anvils R us | 2 ton anvil | 14.99 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | Oil can | 8.99 |
+-------------+----------------+------------+
这里,最大的差别是所指定的两个列(prod_name 和prod_price)在一个表中,而另一个列(vend_name)在另一个表中。
通过where子句连接: where vendors.vend_id = products.vend_id
笛卡儿积(cartesian product) 由没有联结条件的表关系返回的结果为笛卡儿积, 笛卡尔积不是我们想要的结果
mysql> select vend_name, prod_name,prod_price from vendors,products order by vend_name,prod_name; //没有where过滤
内部联结 inner join
下面语句返回与上面相同的结果:
mysql> select vend_name,prod_name, prod_price from vendors inner join products on vendors.vend_id =
--> products.vend_id order by vend_name, prod_name;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Detonator | 13.00 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Anvils R us | .5 ton anvil | 5.99 |
| Anvils R us | 1 ton anvil | 9.99 |
| Anvils R us | 2 ton anvil | 14.99 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | Oil can | 8.99 |
+-------------+----------------+------------+
联结多个表
mysql> select vend_name,prod_name, prod_price from vendors ,orderitems, products where products.vend_id =
--> vendors.vend_id and orderitems.prod_id = products.prod_id and order_num = 20005;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| Anvils R us | .5 ton anvil | 5.99 |
| Anvils R us | 1 ton anvil | 9.99 |
| ACME | TNT (5 sticks) | 10.00 |
| ACME | Bird seed | 10.00 |
+-------------+----------------+------------+