使用数据处理函数
大多数SQL支持以下类型的函数:
1> 文本函数:用于处理文本串(删除或填充值,转换值为大写或小写)
2> 数值函数:用于在数值数据上进行算术操作(返回绝对值,进行代数运算)
3> 日期和时间函数:用于处理日期和时间值并从这些值中提取特定成分(返回两个日期之差,检查日期的有效性)
4> 系统函数:返回DBMS正使用的特殊信息(如返回用户登陆信息,检查版本细节)
1.文本处理函数
SELECT vend_name,Upper(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name; #Upper()将文本转换为大写
常见的的文本处理函数
函数 | 说明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 找出串的一个字串 |
Lower() | 将串转换为小写 |
LTrim() | 去掉串左边的空格 |
RTrim() | 去掉串右边的空格 |
Right() | 返回串右边的字符 |
Soundex() | 返回串的SOUNDEX值 |
Upper() | 将串转换为大写 |
SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX考虑了类似的发音字符和音节,使得能对串进行发音比较而不是字母比较。SELECT cust_name , cust_contact FROM customers WHERE cust_contact = 'Y. Lie'; #联系名实际上是Y. Lie,但是cust_contact中写成了‘Y. Lee’,那么现在无法查找到该联系名
SELECT cust_name, cust_contact FROM customers WHERE Soundex(cust_contact) = Soundex('Y. Lee'); #,WHERE子句使用Soundex()函数来转换cust_contact列值和搜索串为它们的SOUNDEX值。因为Y.Lee和Y.Lie发音相似,所以它们的SOUNDEX值匹配
2.日期和时间处理函数
常见的日期和时间处理函数
函数 | 说明 |
---|---|
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两个日期之差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期的年份部分 |
MySQL使用的日期格式必须为格式yyyy-mm-dd。2005年9月1日,给出为2005-09-01。SELECT cust_id, order_num FROM orders WHERE order_date = '2005-09-01'; #检索订单日期为2005-09-01的订单记录。
如果用当前日期和时间存储订单日期(存储的order_date 值为2005-09-01 11:30:05),则WHERE order_date = '2005-09-01'失败。即使给出具有该日期的一行,也不会把它检索出来,因为WHERE匹配失败。解决办法是指示MySQL仅将给出的日期与列中的日期部分进行比较,而不是将给出的日期与整个列值进行比较。为此,必须使用Date()函数。Date(order_date)指示MySQL仅提取列的日期部分:SELECT order_num,cust_id FROM orders WHERE Date(order_date) = '2005-09-01';
当然,也存在一个Time()函数,在你只想要时间时应该使用它。
SELECT cust_id, order_num FROM orders WHERE Year(order_date) = 2005 AND Month(order_date) = 9; #检索2005年9月的所有订单
还有一种方法:SELECT cust_id, order_num FROM orders WHERE order_date BETWEEN '2005-09-01' AND '2005-09-30';
3.数值处理函数
常用的数值处理函数:
函数 | 说明 |
---|---|
Abs() | 返回一个数的绝对值 |
Cos() | 返回一个角度的余弦 |
Exp() | 返回一个数的指数值 |
Mod() | 返回除操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回一个随机数 |
Sin() | 返回一个角度的正弦 |
Sqrt() | 返回一个数的平方根 |
Tan() | 返回一个角度的正切 |
汇总数据
当需要对数据进行汇总而不是检索表中数据时,MySQL提供了专门的函数。这种例子有以下几种:
1> 确定表中行数(或者满足某个条件或包含某个特定值的行数)
2> 获得表中行组的和
3> 找出表列(或所有行或某些特定的行)的最大值、最小值和平均值
聚集函数(aggregate function),运行在行组上,计算和返回单个值的函数。
函数 | 说明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
1.AVG()函数
AVG()通过对表中行数计数并计算特定列值之和,求得该列的平均值。AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。
SELECT AVG(prod_price) AS avg_prcie FROM products; #返回表中所有产品的平均价格
SELECT AVG(prod_price) AS avg_price FROM products WHERE prod_id = 1003; #返回特定列的平均值
只用于单个列 AVG()只能用来确定特定数值列的平均值,而且列名必须作为函数参数给出。为了获得多个列的平均值,必须使用多个AVG()函数。AVG()函数忽略列值为NULL的行。
2.COUNT()函数
COUNT()函数进行计数。可利用COUNT()确定表中行的数目或符合特定条件的行的数目。
COUNT()函数有两种使用方式:
1> 使用COUNT(*)对表中行的数目进行计数,包含NULL在内
2> 使用COUNT(column)对特定列中具有值的行进行计数,不包含NULL值
SELECT COUNT(*) AS num_cust FROM customers; #这里COUNT对所有行计数
SELECT COUNT(cust_email) AS num_cust FROM customers; #这里COUTN对cust_email)中有值的行进行计数
3.MAX()函数
MAX()返回指定列中的最大值。MAX()要求指定列名SELECT MAX(prod_price) AS max_price FROM products; #返回products中最贵的物品的价格
对非数值数据使用MAX() 虽然MAX()一般用来找出最大的数值或日期值,但MySQL允许将它用来返回任意列中的最大值,包括返回文本列中的最大值。在用于文本数据时,如果数据按相应的列排序,则MAX()返回最后一行。MAX()函数忽略列值为NULL的行。
4.MIN()函数
MIN()的功能正好与MAX()功能相反,它返回指定列的最小值。与MAX()一样,MIN()要求指定列名。SELECT MIN(prod_price) AS min_price FROM products; #MIN()返回products表中最便宜物品的价格。
5.SUM()函数
SUM()用来返回指定列值的和(总计)。
```SELECT SUM(quantity) AS items_orderd
FROM orderitems
WHERE order_num = 20005;
SUM()也可以用来合计计算值。SELECT SUM(quantity*item_price) AS total_price FROM orderitems WHERE order_num = 20005;
利用标准的算术操作符,所有聚集函数都可用来执行多个列上的计算。
5.DISTINCT聚集不同值
以上5个聚集函数都可以如下使用:
1> 对所有的行执行计算,指定ALL参数或不给参数(因为ALL是默认
行为);
2> 只包含不同的值,指定DISTINCT参数。SELECT AVG(DISTINCT prod_price) AS avg_price FROM products WHERE vend_id = 1003;
DISTINCT必须使用列名,不能用于计算或表达式。
6.组合聚集函数
实际上SELECT语句可根据需要包含多个聚集函数:SELECT COUNT(*) AS num_items, MIN(prod_price) AS min_price, MAX(prod_price) AS max_price, AVG(prod_price) AS price_avg FROM products; #注意这里的多个聚集函数之间必须用逗号隔开,否则会报错
分组数据
1.创建分组
分组是在SELECT语句的GROUP BY子句中建立的:SELECT vend_id,COUNT(*) AS num_prods FROM products GROUP BY vend_id; #。GROUP BY子句指示MySQL按vend_id排序并分组数据。
使用WITH ROLLUP关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值:SELECT vend_id, COUNT(*) AS num_prods FROM products GROUP BY vend_id WITH ROLLUP;
2.过滤分组
MySQL为此目的提供了另外的子
句,那就是HAVING子句。HAVING非常类似于WHERE。事实上,目前为止所学过的所有类型的WHERE子句都可以用HAVING来替代。唯一的差别是WHERE过滤行,而HAVING过滤分组。
SELECT cust_id, COUNT(*) AS orders FROM orders GROUP BY cust_id HAVING COUNT(*) >= 2; #HAVING子句,它过滤COUNT(*) >=2(两个以上的订单)的那些分组。
WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。
SELECT vend_id, COUNT(*) AS num_prods FROM products WHERE prod_price >= 10 GROUP BY vend_id HAVING COUNT(*) >= 2;
SELECT vend_id,COUNT(*) AS num_prods FROM products GROUP BY vend_id HAVING COUNT(*) >= 2;
3.分组和排序
GROUP BY和ORDER BY的区别:
ORDER BY | GROUP BY |
---|---|
排序产生的输出 | 分组行。但输出可能不是分组的顺序 |
任意列都可以使用(甚至非选择的列也可以使用) | 只可能使用选择列或表达式列,而且必须使用每个选择列表达式 |
不一定需要 | 如果与聚集函数一起使用列(或表达式),则必须使用 |
SELECT order_num, SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVING SUM(quantity*item_price) >= 50;
SELECT order_num, SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVING SUM(quantity*item_price) >= 50 ORDER BY ordertotal;
4.SELECT 子句顺序
SELECT语句中子句的顺序:
子句 | 说明 |
---|---|
SELECT | 要返回的列或表达式 |
FROM | 从中检索数据的表 |
WHERE | 行级过滤 |
GROUP BY | 分组说明 |
HAVING | 组级过滤 |
ORDER BY | 输出排序顺序 |
LIMIT | 要检索的行数 |
使用子查询
1.利用子查询进行过滤
假如需要列出订购物品TNT2的所有客户,应该怎样检索?
(1) 检索包含物品TNT2的所有订单的编号。
(2) 检索具有前一步骤列出的订单编号的所有客户的ID。
(3) 检索前一步骤返回的所有客户ID的客户信息。
上述每个步骤都可以单独作为一个查询来执行。
SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'; #,对于prod_id为TNT2的所有订单物品,它检索其order_num列
SELECT cust_id FROM orders WHERE order_num IN (20005,20007); #查询具有订单20005和20007的客户ID
对于上面的两个单独的查询,可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。
SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'TNT2');
现在得到了订购物品TNT2的所有客户的ID。下一步是检索这些客户ID的客户信息。SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (10001,10004);
可以把其中的WHERE子句转换为子查询而不是硬编码这些客户id。SELECT cust_name, cust_contact FROM customers WHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'));
把子查询分解为多行并且适当地进行缩进,能极大地简化子查询的使用。
2. 作为计算字段使用子查询
假如需要显示customers表中每个客户的订单总数。订单与相应的客户ID存储在orders表中。
为了执行这个操作,遵循下面的步骤。
(1) 从customers表中检索客户列表。
(2) 对于检索出的每个客户,统计其在orders表中的订单数目。SELECT cust_id, COUNT(order_num) AS num FROM orders GROUP BY cust_id;
可使用SELECT COUNT(*)对表中的行进行计数,并且通过提供一条WHERE子句来过滤某个特定的客户ID,可仅对该客户的订单进行计数。
SELECT COUNT(*) FROM orders WHERE cust_id = 10001; #对客户10001的订单进行计数
为了对每个客户执行COUNT()计算,应该将COUNT()作为一个子查询。SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders WHERE orders.cust_id = customers.cust_id) AS orders FROM customers ORDER BY cust_name;
相关子查询(correlated subquery) 涉及外部查询的子查询。任何时候只要列名可能有多义性,就必须使用这种语法(表名和列名由一个句点分隔)。
联接表
1.创建联结
联结的创建非常简单,规定要联结的所有表以及它们如何关联即可。SELECT vend_name, prod_name,prod_price FROM vendors, products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name,prod_name;
2.内部联结
目前为止所用的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内部联结。其实,对于这种联结可以使用稍微不同的语法来明确指定联结的类型。下面的SELECT语句返回与前面例子完全相同的数据:
SELECT vend_name, prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
这里,两个表之间的关系是FROM子句的组成部分,以INNERJOIN指定。在使用这种语法时,联结条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。
3.联结多个表
SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系。SELECT vend_name,prod_name, prod_price,quantity FROM orderitems,vendors, products WHERE products.vend_id = vendors.vend_id AND orderitems.prod_id = products.prod_id AND order_num = 20005; #此例子显示编号为20005的订单中的物品。
返回订购产品TNT2的客户列表:
```SELECT cust_id, cust_name,cust_contact,prod_id
FROM orderitems,orders,customers
WHERE customers.cust_id = orders.cust_id
AND