[TOC]
本部分主要涉及到MySQL进行简单的四则计算,处理数值,文本和日期与时间的各种函数。常见的聚集函数求均值,最值等,最后是分组处理。
第十章 创建计算字段
为什么要创建计算字段呢?通常来讲,存储在数据库中的数据往往都不是应用程序所需要的,比如说我们想要获得每个月的收益值,那么就不需要从数据库中获取一个月当中每一天的收益,而只需要对当月数据进行求和即可,这样就可以最大程度地减轻访问数据库的压力。
计算字段是运行时在SELECT语句内创建的。
字段:基本与列的意思相同,经常互换使用,不过数据库一般称为列,而属于字段通常用在计算字段的连接上。
10.1 拼接操作
例如:在vendors表中包含供应商名和位置信息,我们想要生成一个供应商的报表,需要在供应商的名字中按照name(location)这样的格式列出供应商的位置。我们需要一个拼接操作
在vendors表中有两个列,分别是vend_name和vend_country。
- 拼接(concatenate)将值连接到一起构成单个值
- 口令是Concat(在其他DBMS中,会用+或者||进行拼接)
1 | SELECT Concat(vend_name,' (',vend_country,')') FROM vendors |
- RTrim是用来删除值右侧的所有空格(LTrim用来删除左侧所有的空格以及Trim删除两侧的空格)
1 | SELECT Concat(RTrim(vend_name),' (',RTrim(vend_country),')') FROM vendors |
- 上述操作,我们只是简单地看一眼当然没有什么问题,新值只是被计算出来了而已,并没有被实际命名,我们自然也就不能引用,于是我们要给他一个别名。
1 | SELECT Concat(RTrim(vend_name),' (',RTrim(vend_country),')') AS vend_title |
- 别名的另一个作用就是当列名不符合规范或者名字不好时,可以通过别名达到重新命名的结果。
10.2 执行算术计算
1 | SELECT prod_id,quantity,item_price |
我们首先获取orderitems表中的订单数量,物品价格。此时我们想要知道订单的总价是多少,即数量乘以单价。
1 | SELECT prod_id,quantity,item_price,quantity*item_price AS expanded_price |
- 我们通过*得到了一个新的计算字段。四则运算都可以,圆括号用来区分优先顺序。
第十一章 使用数据处理函数
本章介绍什么是函数,MySQL支持何种函数以及如何使用这些函数。
函数:function, 和其他程序语言一样,函数就是实现某个具体功能的代码块。
注意:函数的可移植性不好,所以往往在不同的DBMS之间差异很大,所以如果要使用函数的话们应该做好代码注释。
11.1 使用不同类型的函数
函数主要包括以下几种类型:
- 用于处理文本串,如删除或填充值,转化为大写或小写;
- 数值运算函数,例如绝对值,指数,对数之类的;
- 处理日期和时间值的函数;
- 返回DBMS的特殊信息,比如用户登录信息,版本信息等系统函数。
11.1.1 文本处理函数
上一节中,我们用到了RTrim和Concat函数,我们再来看几个,
- 将文本转化为大写;
1 | SELECT vend_name,Upper(vend_name) AS vend_name_upcase |
- Left(str,len): 返回串str左边长度为Len的字符串;(Right)
1 | SELECT Left("abc",2); /* 返回字符串ab */ |
Length: 返回串的长度;
Locate: 格式为LOCATE(substr,str), LOCATE(substr,str,pos);
1
2SELECT LOCATE('bar', 'foobarbar');
-> 4 -- 返回4,即第一次bar出现的位置1
2SELECT LOCATE('xbar', 'foobar');
-> 0 -- 返回0,是因为查不到1
2SELECT LOCATE('bar', 'foobarbar', 5);
-> 7 -- 从第5个位置开始索引,找到第一个bar的起始位置LTrim,RTrim,Upper,Lower.
Soundex: 返回串的SOUNDEX值,使能够对串进行发音比较而不是字母比较。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25SELECT cust_name,cust_contact
FROM customers
WHERE cust_contact = 'Y. Lie';
-> Empty set -- 返回空集合,不会返回数据,这是因为名字写错了,应该是Y Lee
SELECT cust_name,cust_contact
FROM customers
WHERE cust_contact = 'Y Lee'; -- 则会出现返回值
+-------------+--------------+
| cust_name | cust_contact |
+-------------+--------------+
| Coyote Inc. | Y Lee |
+-------------+--------------+
-- 为了避免这种情况,我们采用发音比较的方式
SELECT cust_name,cust_contact
FROM customers
WHERE Soundex(cust_contact) = Soundex('Y. Lie'); -- 此时返回Y Lee的记录,发音上比较相似
+-------------+--------------+
| cust_name | cust_contact |
+-------------+--------------+
| Coyote Inc. | Y Lee |
+-------------+--------------+
SubString: 格式为下述之一,起到取子串的作用。
SUBSTRING(str,pos), SUBSTRING(str FROM pos), SUBSTRING(str,pos,len),
SUBSTRING(str FROM pos FOR len)
11.1.2 日期和事件处理函数
日期和时间采用相应的数据类型和特殊的格式存储。一般地,应用程序不使用用来存储日期和时间的格式,因此日期和时间函数总是被用来读取,统计和处理这些值,由于这个原因,日期和时间函数在MySQL语言中具有重要的作用。
日期和时间函数有以下几类:
- 获取当前时间的信息,Now, CurDate(Current),CurTime;
- 获取某个日期的具体信息, Date,Time, Year, Month, Day, Hour, Minute, Second, DayOfWeek(星期几);
- 日期的添删运算AddDate, AddTime, DateDiff, Date_Add, Date_Format.
| 函数 | 用途 | 示例 | 输出 |
|---|---|---|---|
| AddDate | 增加日期(比给出日期变化多少) | SELECT ADDDATE(‘2008-01-02’, INTERVAL 31DAY); SELECT ADDDATE(‘2008-01-02’, 31); | 2008-02-02 |
| AddTime | 增加时间 | ||
| CurDate | 返回当前日期 | SELECT CurDate(); | 2020-04-03 |
| CurTime | 返回当前时间 | SELECT CurTime(); | 15:54:33 |
| Date | 返回日期部分 | SELECT Date(‘2020-04-03 15:54:33’); | 2020-04-03 |
| DateDiff | 日期之差 | SELECT DATEDIFF(‘2007-12-31 23:59:59’,’2007-12-30’); | 1 |
| Date_Add | 灵活的日期运算函数(可以加减小时分钟) | SELECT DATE_ADD(‘2008-01-02’, INTERVAL 31 DAY); | 2008-02-02 |
| Date_Format | 日期格式化 | SELECT DATE_FORMAT(‘2009-10-04 22:23:00’, ‘%W %M %Y’); | ‘Sunday October 2009’ |
| Day | 返回某个日期的天 | SELECT Day(‘2020-04-03 15:54:33’); | 3 |
| DayOfWeek | 返回某个日期是星期几 | SELECT DAYOFWEEK(‘2020-04-03’); | 6(星期天是第一天) |
| Hour | 返回某个日期的小时 | SELECT Hour(‘2020-04-03 15:54:33’); | 15 |
| Minute | 返回某个日期的分钟 | SELECT Minute(‘2020-04-03 15:54:33’); | 54 |
| Month | 返回某个日期的月份 | SELECT Month(‘2020-04-03 15:54:33’); | 4 |
| Now | 返回当前日期和时间 | SELECT now(); | 2020-04-03 15:54:33 |
| Second | 返回某个日期的秒 | SELECT second(‘2020-04-03 15:54:33’); | 33 |
| Time | 返回时间部分 | SELECT Time(‘2020-04-03 15:54:33’); | 15:54:33 |
| Year | 返回某个日期的年份 | SELECT Year(‘2020-04-03 15:54:33’); | 2020 |
我们看一些具体例子。
首选日期格式 yyyy-mm-dd;
给出完整的4位数字年份更加可靠;
1 | SELECT cust_id,order_num |
我们可以发现order_date为2005-09-01虽然被找出来了,但是一旦有时间的信息,比如2005-09-01 11:30:05则匹配失败,我们也不可能记得如此精确地时间进行检索,所以通常我们会比较列中日期的一部分,为此,可以用Date函数进行日期的比较。
1 | SELECT cust_id,order_num |
另一种场景是当我们想要获得9月份所有的订单信息,可以比较年份和月份,也可以指定日期范围。
1 | SELECT cust_id,order_num |
11.1.3 数值处理函数
常见的代数,三角函数,几何运算。
Abs, Cos, Exp, Mod, Pi, Rand, Sin, Sqrt, Tan估计看名字也就知道意思了。
1 | SELECT Sin(Pi()); |
第十二章 汇总数据
本章介绍什么是SQL的聚集函数以及如何利用他们汇总表的数据。
12.1 聚集函数
我们通常需要汇总数据而不是直接将他们检索出来。有如下几个方面的例子,例如
- 确定表中的行数,例如9月份订单总数
- 确定行的和,比如9月份消费总金额
- 找出列中的最大值,最小值,平均值等
聚集函数(aggregate function)运行在行组上,计算和返回单个值的函数。
| 函数 | 说明 |
|---|---|
| AVG() | 返回某列的平均值 |
| COUNT() | 返回某列的行数 |
| MAX() | 返回某列的最大值 |
| MIN() | 返回某列的最小值 |
| SUM() | 返回某列值之和 |
12.2 常见聚集函数的示例
12.2.1 AVG均值函数
1 | SELECT AVG(prod_price) AS avg_price |
注意:
- avg只能用于一列的计算,如果要计算多列,则需要多个avg语句;
- avg会忽略值为NULL的行。
12.2.2 COUNT计数函数
返回表中行的数目,或者满足条件的行的数目。
- COUNT(*)对表中行的数目进行技术,不管表列中包含的是空值还是非空值;
- 使用COUNT(column)对特定的列中具有值的行进行技术,忽略NULL值。
1 | SELECT COUNT(*) AS num_cust |
12.2.3 MAX,MIN最值函数
返回指定列中的最大值,忽略NULL。
1 | SELECT MAX(prod_price) AS max_price |
12.2.4 SUM求和函数
用法类似于求均值,只是不用除以求和的总个数了。
12.3 聚集函数的其他用法
对以上5个聚集函数在使用时,
- 对所有的行执行计算,指定ALL参数或者不给参数(默认ALL)
- 只包含不同的值,指定DISTINCT参数
1 | SELECT AVG(DISTINCT prod_price) AS avg_price |
- SELECT语句可包含多个聚集函数
1 | SELECT COUNT(*) AS num_items, |
第十三章 分组数据
本章介绍如何分组数据,以便能汇总表内容的子集,这涉及两个新SELECT语句子句,分别是GROUP BY和HAVING子句。
1 | SELECT COUNT(*) AS num_prods |
Group by 可以包含任意数目的列,这使得能对分组进行嵌套,实施更加精细的分组;
在建立分组时,Group by后面的所有都参与分组,只要有一个列取值不同,就可以作为一个分组;
Group by后面的列必须是有效的表达式,不能是聚集函数,若在select中使用表达式,则group by中使用相同的表达式,不能使用别名;
1
select vend_id, vend_id*prod_price as alias from products group by vend_id,alias;
除聚集语句外,在select后面的所有列都必须出现在group by中;
1
2select vend_id from products group by prod_price; -- 错误
select vend_id from products group by vend_id; -- 成功
如果分组具有NULL值,则所有具有NULL值的行将作为一组;
Group by子句出现在WHERE之后,ORDER BY之前。
1 | SELECT vend_id, COUNT(*) AS num_prods |
13.1 过滤分组
假设我们想要知道在orders表中,每位顾客的订单数,只需要按顾客编号进行分组,然后用count计数即可。但如果我们的需求是找出买了至少两个东西的顾客呢,此时分组就不够了,我们还要对分组进行过滤,找出计数数不小于2的分组,此时用HAVING子句。
1 | SELECT cust_id,COUNT(*) AS orders |
13.1.1 HAVING与WHERE的差别
在之前章节中,HAVING完全可以替代WHERE.
1 | SELECT cust_id FROM orders HAVING cust_id = 10001; |
但是,HAVING可以用在分组处理中,而where是对所有的行进行过滤。
另外一种理解方式,就是where首先对数据进行过滤,排除不需要的行,然后group by对剩下的行进行分组,最后是having进行分组过滤。来看如下例子:
1 | SELECT vend_id,COUNT(*) AS num_prods |
13.2 分组和排序
分组Group by是分组行,但输出不一定是什么顺序, 排序Order by是排序产生的输出。我们有时候在分组汇总之后,还要对分组的顺序加以限定,于是就出现了分组和排序的组合。
1 | SELECT order_num,SUM(quantity*item_price) AS ordertotal |
13.3 SELECT子句顺序
| 子句 | 说明 | 是否必须 |
|---|---|---|
| SELECT | 选择列或表达式 | 是 |
| FROM | 从表 | 仅在从表中选择数据时使用 |
| WHERE | 行过滤 | 否 |
| GROUP BY | 分组 | 仅在按组计算聚集时使用 |
| HAVING | 按组过滤 | 否 |
| ORDER BY | 对输出排序 | 否 |
| LIMIT | 限制检索行数 | 否 |