SELECT查询语句
1、单个表
SELECT 列名1[,列名2,...]/*/[DISTINCT] 列名1[,列名2,...]/Concat(列名1,,'(,列名2,...)') AS 别名/列名1 +、-、*、/ 列名2
FROM 表名
[WHERE 过滤条件 LIKE/REGEXP 搜索模式(通配符)/正则表达式] [AND/OR/IN/NOT 过滤条件[,过滤条件...]],
[GROUP BY 列名],
[HAVING 分组过滤条件],
[ORDER BY 列名m[,列名n,...] [DESC/ASC]], #ORDER BY 子句,必须位于 FROM 子句和 WHERE 子句之后
[LIMIT n], #显示行数,必须位于 ORDRE BY 子句之后
1.1、检索数据
1.1.1、检索单个列
mysql> SELECT prod_name FROM products;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
| Bird seed |
| Carrots |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)
MySQL 不区分大小写,忽略空格,以分号结尾。
1.1.2、检索多个列
mysql> SELECT prod_id,prod_name,prod_price
-> FROM products;
+---------+----------------+------------+
| prod_id | prod_name | prod_price |
+---------+----------------+------------+
| ANV01 | .5 ton anvil | 5.99 |
| ANV02 | 1 ton anvil | 9.99 |
| ANV03 | 2 ton anvil | 14.99 |
| DTNTR | Detonator | 13.00 |
| FB | Bird seed | 10.00 |
| FC | Carrots | 2.50 |
| FU1 | Fuses | 3.42 |
| JP1000 | JetPack 1000 | 35.00 |
| JP2000 | JetPack 2000 | 55.00 |
| OL1 | Oil can | 8.99 |
| SAFE | Safe | 50.00 |
| SLING | Sling | 4.49 |
| TNT1 | TNT (1 stick) | 2.50 |
| TNT2 | TNT (5 sticks) | 10.00 |
+---------+----------------+------------+
14 rows in set (0.00 sec)
1.1.3、检索所有行
mysql> SELECT *
-> FROM products;
+---------+---------+----------------+------------+----------------------------------------------------------------+
| prod_id | vend_id | prod_name | prod_price | prod_desc |
+---------+---------+----------------+------------+----------------------------------------------------------------+
| ANV01 | 1001 | .5 ton anvil | 5.99 | .5 ton anvil, black, complete with handy hook |
| ANV02 | 1001 | 1 ton anvil | 9.99 | 1 ton anvil, black, complete with handy hook a
...
1.1.4、检索不同的行
如果你不想每个值都出现,怎么办?例如,假如你想得到 products 表中产品的所有供应商 ID:
mysql> SELECT vend_id
-> FROM products;
+---------+
| vend_id |
+---------+
| 1001 |
| 1001 |
| 1001 |
| 1002 |
| 1002 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1003 |
| 1005 |
| 1005 |
+---------+
14 rows in set (0.00 sec)
#返回了 14 行,因为 products 表中列出了 14 个产品,那么如何检索不同值的列表呢?
mysql> SELECT DISTINCT vend_id
-> FROM products;
+---------+
| vend_id |
+---------+
| 1001 |
| 1002 |
| 1003 |
| 1005 |
+---------+
4 rows in set (0.00 sec)
IGNORE 关键字应用于所有的列,而不是前置它的列。
1.1.5、限制结果
mysql> SELECT prod_name
-> FROM products
-> LIMIT 5;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Detonator |
| Bird seed |
+--------------+
5 rows in set (0.00 sec)
为了得到下一个 5 行,可指定检索的开始行和行数:
mysql> SELECT prod_name
-> FROM products
-> LIMIT 5,5; #第一个数是开始的位置,第二个数为要检索的行数
+--------------+
| prod_name |
+--------------+
| Carrots |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
+--------------+
5 rows in set (0.00 sec)
检索出来的第一行为 行0 而不是 行1,因此,LIMIT 1,将检索出来第 2 行。
1.1.6、使用完全限定的表名
mysql> SELECT DISTINCT products.vend_id
-> FROM li.products;
+---------+
| vend_id |
+---------+
| 1001 |
| 1002 |
| 1003 |
| 1005 |
+---------+
4 rows in set (0.00 sec)
1.2、排序检索数据
1.2.1、排序数据
mysql> SELECT prod_name
-> FROM products
-> ORDER BY prod_name;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Bird seed |
| Carrots |
| Detonator |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)
1.2.2、按多个列排序
mysql> SELECT prod_id,prod_price,prod_name
-> FROM products
-> ORDER BY prod_price,prod_name;
+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
| FU1 | 3.42 | Fuses |
| SLING | 4.49 | Sling |
| ANV01 | 5.99 | .5 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV02 | 9.99 | 1 ton anvil |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| DTNTR | 13.00 | Detonator |
| ANV03 | 14.99 | 2 ton anvil |
| JP1000 | 35.00 | JetPack 1000 |
| SAFE | 50.00 | Safe |
| JP2000 | 55.00 | JetPack 2000 |
+---------+------------+----------------+
14 rows in set (0.00 sec)
仅在多个行具有相同的 prod_price 时才会对产品按 prod_name 排序。如果 prod_price 唯一,则不会按照 pord_name 排序。
1.2.3、指定排序方向
mysql> SELECT prod_id,prod_price,prod_name
-> FROM products
-> ORDER BY prod_price DESC;
+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| JP2000 | 55.00 | JetPack 2000 |
| SAFE | 50.00 | Safe |
| JP1000 | 35.00 | JetPack 1000 |
| ANV03 | 14.99 | 2 ton anvil |
| DTNTR | 13.00 | Detonator |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| ANV02 | 9.99 | 1 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV01 | 5.99 | .5 ton anvil |
| SLING | 4.49 | Sling |
| FU1 | 3.42 | Fuses |
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
+---------+------------+----------------+
14 rows in set (0.00 sec)
默认为升序排序,还可以指定降序排序。DESC 只能作用于前置它的列名。
1.3、过滤数据
1.3.1、WHERE 子句的操作符
WHERE 子句的操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
BETWEEN | 在指定的两个值之间 |
mysql> SELECT prod_name,prod_price
-> FROM products
-> WHERE prod_name = 'fuses';
+-----------+------------+
| prod_name | prod_price |
+-----------+------------+
| Fuses | 3.42 |
+-----------+------------+
1 row in set (0.00 sec)
#列出价格位于 5 和 10 美元之间的所有产品
mysql> SELECT prod_name,prod_price
-> FROM products
-> WHERE prod_price BETWEEN 5 AND 10;
+----------------+------------+
| prod_name | prod_price |
+----------------+------------+
| .5 ton anvil | 5.99 |
| 1 ton anvil | 9.99 |
| Bird seed | 10.00 |
| Oil can | 8.99 |
| TNT (5 sticks) | 10.00 |
+----------------+------------+
5 rows in set (0.00 sec)
#NULL 值检查
mysql> SELECT cust_id,cust_name
-> FROM customers
-> WHERE cust_email IS NULL;
+---------+-------------+
| cust_id | cust_name |
+---------+-------------+
| 10002 | Mouse House |
| 10005 | E Fudd |
+---------+-------------+
2 rows in set (0.00 sec)
1.3.2、组合 WHERE 子句
- AND 操作符,为了通过不止一个列进行过滤,用来指示检索满足所有给定条件的行
#检索由供应商 1003 提供的且价格小于等于 10 美元的商品
mysql> SELECT prod_id,prod_price,prod_name
-> FROM products
-> WHERE vend_id = 1003 AND prod_price <= 10;
+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| FB | 10.00 | Bird seed |
| FC | 2.50 | Carrots |
| SLING | 4.49 | Sling |
| TNT1 | 2.50 | TNT (1 stick) |
| TNT2 | 10.00 | TNT (5 sticks) |
+---------+------------+----------------+
5 rows in set (0.00 sec)
- OR 操作符,它指示检索匹配任意条件的行
mysql> SELECT prod_name,prod_price
-> FROM products
-> WHERE vend_id = 1002 OR vend_id = 1003;
+----------------+------------+
| prod_name | prod_price |
+----------------+------------+
| Detonator | 13.00 |
| Bird seed | 10.00 |
| Carrots | 2.50 |
| Fuses | 3.42 |
| Oil can | 8.99 |
| Safe | 50.00 |
| Sling | 4.49 |
| TNT (1 stick) | 2.50 |
| TNT (5 sticks) | 10.00 |
+----------------+------------+
9 rows in set (0.00 sec)
当出现多个 AND 或者 OR 的过滤条件时,推荐使用圆括号指定计算次序。
- IN 操作符,用来指定条件范围,范围内的每个条件都可以进行匹配
mysql> SELECT prod_name,prod_price
-> FROM products
-> WHERE vend_id IN (1002,1003)
-> ORDER BY prod_name;
+----------------+------------+
| prod_name | prod_price |
+----------------+------------+
| Bird seed | 10.00 |
| Carrots | 2.50 |
| Detonator | 13.00 |
| Fuses | 3.42 |
| Oil can | 8.99 |
| Safe | 50.00 |
| Sling | 4.49 |
| TNT (1 stick) | 2.50 |
| TNT (5 sticks) | 10.00 |
+----------------+------------+
9 rows in set (0.00 sec)
- NOT 操作符,用来否定他之后的任何条件
mysql> SELECT prod_name,prod_price
-> FROM products
-> WHERE vend_id NOT IN (1002,1003)
-> ORDER BY prod_name;
+--------------+------------+
| prod_name | prod_price |
+--------------+------------+
| .5 ton anvil | 5.99 |
| 1 ton anvil | 9.99 |
| 2 ton anvil | 14.99 |
| JetPack 1000 | 35.00 |
| JetPack 2000 | 55.00 |
+--------------+------------+
5 rows in set (0.00 sec)
1.4、用通配符进行过滤
通配符:用来匹配值的一部分的特殊字符
搜索模式:由字面值、通配符或两者组成的搜索条件,搭配 LIKE 谓词
1.4.1、百分号(%)通配符
% 表示任何字符出现任意次数
#以 jet 开头
mysql> SELECT prod_id,prod_name
-> FROM products
-> WHERE prod_name LIKE 'jet%';
+---------+--------------+
| prod_id | prod_name |
+---------+--------------+
| JP1000 | JetPack 1000 |
| JP2000 | JetPack 2000 |
+---------+--------------+
2 rows in set (0.00 sec)
#任意位置出现 anvil
mysql> SELECT prod_id,prod_name
-> FROM products
-> WHERE prod_name LIKE '%anvil%';
+---------+--------------+
| prod_id | prod_name |
+---------+--------------+
| ANV01 | .5 ton anvil |
| ANV02 | 1 ton anvil |
| ANV03 | 2 ton anvil |
+---------+--------------+
3 rows in set (0.00 sec)
#以 s 开头以 e 结尾
mysql> SELECT prod_id,prod_name
->
-> FROM products
-> WHERE prod_name LIKE 's%e';
+---------+-----------+
| prod_id | prod_name |
+---------+-----------+
| SAFE | Safe |
+---------+-----------+
1 row in set (0.00 sec)
1.4.2、下划线(_)通配符
下划线(_)只能匹配单个字符而不是多个字符
mysql> SELECT prod_id,prod_name
-> FROM products
-> WHERE prod_name LIKE '_ ton anvil';
+---------+-------------+
| prod_id | prod_name |
+---------+-------------+
| ANV02 | 1 ton anvil |
| ANV03 | 2 ton anvil |
+---------+-------------+
2 rows in set (0.00 sec)
#与 % 作比较
mysql> SELECT prod_id,prod_name
-> FROM products
-> WHERE prod_name LIKE '%ton anvil';
+---------+--------------+
| prod_id | prod_name |
+---------+--------------+
| ANV01 | .5 ton anvil |
| ANV02 | 1 ton anvil |
| ANV03 | 2 ton anvil |
+---------+--------------+
3 rows in set (0.00 sec)
1.5、使用正则表达式进行搜索
1.5.1、基本字符匹配
. 是正则表达式语言中一个特殊的字符。它代表匹配任意一个字符
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '.000'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)
注意:LIKE 和 REGEXP 之间有一个很重要的差别。如下:
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name LIKE '1000'
-> ORDER BY prod_name;
Empty set (0.00 sec)
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '1000'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
+--------------+
1 row in set (0.00 sec)
LIKE 匹配整个列。如果匹配的文本在列值中出现,LIKE 将不会找到它,相应的行也不会被返回(除非使用通配符)。而 REGEXP 在列值内进行匹配,如果被匹配的文本在列值中出现,REGEXP 就会找到它,并返回相应的行。
1.5.2、进行 OR 匹配
| 为正则表达式的 OR 操作符,它代表匹配其中之一
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '1000|2000'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)
1.5.3、匹配几个字符之一
[n1,n2,…] 代表其中的任意一个字符
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[123] Ton'
-> ORDER BY prod_name;
+-------------+
| prod_name |
+-------------+
| 1 ton anvil |
| 2 ton anvil |
+-------------+
2 rows in set (0.00 sec)
1.5.4、匹配范围
[n1- n2] 代表匹配任意从 n1 到 n2 之间的字符
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[1-5] Ton'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
+--------------+
3 rows in set (0.00 sec)
1.5.5、匹配特殊字符
如果想找出包含 . 字符的值,怎样搜索?必须使用前导符 \\,称之为转义
mysql> SELECT vend_name
-> FROM vendors
-> WHERE vend_name REGEXP '\\.'
-> ORDER BY vend_name;
+--------------+
| vend_name |
+--------------+
| Furball Inc. |
+--------------+
1 row in set (0.00 sec)
1.5.6、匹配多个实例
元字符 | 说明 |
---|---|
* | 0 个或多个匹配 |
+ | 1 个或多个匹配 |
? | 0 个或 1 个匹配 |
{n} | 指定数目的匹配 |
{n,} | 不少于指定数目的匹配 |
{n,m} | 匹配数目的范围 |
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '\\([0-9] sticks?\\)'
-> ORDER BY prod_name;
+----------------+
| prod_name |
+----------------+
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
2 rows in set (0.00 sec)
1.5.7、匹配字符类
类 | 说明 |
---|---|
[:alnum:] | 任意字母和数字(同 [a-zA-Z0-9]) |
[:alpha:] | 任意字符(同[a-zA-Z]) |
[:blank:] | 空格和制表(同 [\\t]) |
[:cntrl:] | ASCII 控制字符(ASCII 0 到 31 到 127) |
[:digit:] | 任意数字(同 [0-9]) |
[:lower:] | 任意小写字母(同 [a-z]) |
[:graph:] | 与 [:print:] 相同,但不包含空格 |
[:print:] | 任意可打印字符 |
[:punct:] | 既不在 [:alnum:] 又不在 [:cntrl:] 中的任意字符 |
[:space:] | 包括空格在内的任意空白字符(同 [\\f\\n\\r\\t\\v]) |
[:upper:] | 任意大写字母(同 [A-Z]) |
[:xdigit:] | 任意十六进制数字(同 [a-fA-F0-9]) |
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[[:digit:]]{4}'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)
1.5.8、定位符
元字符 | 说明 |
---|---|
^ | 文本的开始 |
$ | 文本的结尾 |
[[:<:]] | 词的开始 |
[[:>:]] | 词的结尾 |
mysql> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '^[0-9\\.]'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
+--------------+
3 rows in set (0.00 sec)
1.6、创建计算字段
1.6.1、拼接字段
拼接:将值联结在一起构成单个值,可使用 Concat() 函数来拼接。
mysql> SELECT Concat(vend_name,'(,vend_country,)')
-> FROM vendors
-> ORDER BY vend_name;
+--------------------------------------+
| Concat(vend_name,'(,vend_country,)') |
+--------------------------------------+
| ACME(,vend_country,) |
| Anvils R Us(,vend_country,) |
| Furball Inc.(,vend_country,) |
| Jet Set(,vend_country,) |
| Jouets Et Ours(,vend_country,) |
| LT Supplies(,vend_country,) |
+--------------------------------------+
6 rows in set (0.00 sec)
1.6.2、使用别名
mysql> SELECT Concat(vend_name,'(,vend_country,)') AS vend_title
-> FROM vendors
-> ORDER BY vend_name;
+--------------------------------+
| vend_title |
+--------------------------------+
| ACME(,vend_country,) |
| Anvils R Us(,vend_country,) |
| Furball Inc.(,vend_country,) |
| Jet Set(,vend_country,) |
| Jouets Et Ours(,vend_country,) |
| LT Supplies(,vend_country,) |
+--------------------------------+
6 rows in set (0.00 sec)
1.6.3、执行算数计算
支持的算术运算符有:+、-、*、/
mysql> SELECT prod_id,
-> quantity,
-> item_price,
-> quantity*item_price AS expanded_price
-> FROM orderitems
-> WHERE order_num = 20005;
+---------+----------+------------+----------------+
| prod_id | quantity | item_price | expanded_price |
+---------+----------+------------+----------------+
| ANV01 | 10 | 5.99 | 59.90 |
| ANV02 | 3 | 9.99 | 29.97 |
| TNT2 | 5 | 10.00 | 50.00 |
| FB | 1 | 10.00 | 10.00 |
+---------+----------+------------+----------------+
4 rows in set (0.01 sec)
1.7、使用数据处理函数
1.7.1、文本处理函数
函数 | 说明 |
---|---|
Left() | 返回字符串左边的字符 |
Length() | 返回字符串的长度 |
Locate() | 找出串的一个子串 |
Lower() | 小写 |
LTrim() | 去掉串左边的空格 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
Soundex() | 返回串的SOUNDEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转换为大写 |
1.7.2、日期与时间处理函数
函数 | 说明 |
---|---|
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两日期的差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期的年份部分 |
如果是日期请使用 Date():
mysql> SELECT cust_id,order_num
-> FROM orders
-> WHERE Date(order_date) = '2005-09-01';
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
| 10001 | 20005 |
+---------+-----------+
1 row in set (0.00 sec)
如果想检索出 2005 年 9 月下的所有订单:
mysql> SELECT cust_id,order_num
-> FROM orders
-> WHERE Year(order_date) = 2005 AND Month(order_date) = 9;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
| 10001 | 20005 |
| 10003 | 20006 |
| 10004 | 20007 |
+---------+-----------+
3 rows in set (0.00 sec)
1.7.3、数值处理函数
函数 | 说明 |
---|---|
Abs() | 绝对值 |
Cos() | 余弦 |
Exp() | 指数值 |
Mod() | 余数 |
Pi() | 圆周率 |
Rand() | 随机数 |
Sin() | 正弦 |
Sqrt() | 平方根 |
Tan() | 正切 |
1.8、汇总函数
1.8.1、聚集函数
聚集函数:运行在行组上,计算和返回单个值的函数。
函数 | 说明 |
---|---|
AVG() | 某列的平均值 |
COUNT() | 某列的行数 |
MAX() | 某列的最大值 |
MIN() | 某列的最小值 |
SUM() | 某列值的和 |
1.8.2、AVG() 函数
mysql> SELECT AVG(prod_price) AS avg_price
-> FROM products;
+-----------+
| avg_price |
+-----------+
| 16.133571 |
+-----------+
1 row in set (0.00 sec)
AVG() 函数只能作用于单列,而且忽略值为 NULL 的值。
1.8.3、COUNT() 函数
有两种用法:
- 使用 COUNT(*) 对表中行的数目进行计数,不管表列中包含的是空值还是非空值;
- 使用 COUNT(column) 对特定的列中具有值的行进行计数,忽略 NULL 的值。
mysql> SELECT COUNT(*) AS num_cust
-> FROM customers;
+----------+
| num_cust |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec)
1.8.4、组合聚集函数
mysql> SELECT COUNT(*) AS num_items,
-> MIN(prod_price) AS price_min,
-> MAX(prod_price) AS price_max,
-> AVG(prod_price) AS price_avg
-> FROM products;
+-----------+-----------+-----------+-----------+
| num_items | price_min | price_max | price_avg |
+-----------+-----------+-----------+-----------+
| 14 | 2.50 | 55.00 | 16.133571 |
+-----------+-----------+-----------+-----------+
1 row in set (0.00 sec)
1.9、分组数据
1.9.1、数据分组
从上一节可知,SQL 聚集函数可用来汇总数据,这使得我们能够对行进行计数,计算和与平均值,获得最大和最小值等。如下,如果要返回供应商 1003 提供的产品数目:
mysql> SELECT COUNT(*) AS num_prods
-> FROM products
-> WHERE vend_id = 1003;
+-----------+
| num_prods |
+-----------+
| 7 |
+-----------+
1 row in set (0.00 sec)
但是如果想要返回每个供应商的产品数目该怎么办?如下:
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 |
+---------+-----------+
4 rows in set (0.00 sec)
GROUP BY 子句指示 MySQL 按 vend_id 排序并分组数据,这导致对每个 vend_id 而不是整个表计算 num_prods 一次。GROUP BY 子句指示 MySQL 分组数据,然后对每个组而不是整个结果集进行聚集。在具体使用 GROUP BY 之前,需要知道的一些重要规定:
- GROUP BY 子句可以包含任意数目的列
- 如果 GROUP BY 子句中嵌套了分组,数据将在最后规定的分组进行汇总
- GROUP BY 子句中列出的每个列都必须是检索列或有效的表达式(但不能是聚集函数)
- 除了聚集计算语句外,SELECT 语句中的每个列都必须在 GROUP BY 子句中给出
- 如果分组列中具有 NULL 值,则 NULL 将作为一个分组返回
- GROUP BY 子句必须出现在 WHERE 子句之后,ORDER BY 子句之前
使用 WITH ROLLUP 关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值:
mysql> SELECT vend_id,COUNT(*) AS num_prods
-> FROM products
-> GROUP BY vend_id WITH ROLLUP;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| 1001 | 3 |
| 1002 | 2 |
| 1003 | 7 |
| 1005 | 2 |
| NULL | 14 |
+---------+-----------+
5 rows in set (0.00 sec)
1.9.2、过滤分组
WHERE 过滤行,HAVING 过滤分组。还可以理解为,WHERE 在分组之前过滤,HAVING 在分组之后过滤。
例如,我们想得到至少有两个订单的所有顾客:
mysql> SELECT cust_id,COUNT(*) AS orders
-> FROM orders
-> GROUP BY cust_id
-> HAVING COUNT(*) >= 2;
+---------+--------+
| cust_id | orders |
+---------+--------+
| 10001 | 2 |
+---------+--------+
1 row in set (0.00 sec)
那能不能同时拥有 WHERE 和 HAVING 子句呢?当然可以,如,我们想得到具有 2 个以上、价格为 10 以上的产品供应商:
mysql> SELECT vend_id,COUNT(*) AS num_prod
-> FROM products
-> WHERE prod_price >= 10
-> GROUP BY vend_id
-> HAVING COUNT(*) >= 2;
+---------+----------+
| vend_id | num_prod |
+---------+----------+
| 1003 | 4 |
| 1005 | 2 |
+---------+----------+
2 rows in set (0.00 sec)
2、多表查询
2.1、使用子查询
2.1.1、利用子查询进行过滤
例如,我想列出订购 TNT2 的所有客户:
- 检索包含物品 TNT2 的所有订单的编号;
- 检索具有前一步骤列出的订单编号的所有客户 ID;
- 检索前一步返回的所有客户 ID 的客户信息。
mysql> 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'));
+----------------+--------------+
| cust_name | cust_contact |
+----------------+--------------+
| Coyote Inc. | Y Lee |
| Yosemite Place | Y Sam |
+----------------+--------------+
2 rows in set (0.00 sec)
使用嵌套子句时,列必须匹配。
2.1.2、作为计算字段使用子查询
例如,需要显示 customers 表中每个客户的订单总数:
- 从 customers 表中检索客户列表;
- 对于检索出的每个客户,统计其在 orders 表中的订单数目。
mysql> SELECT cust_name,cust_state,(
-> SELECT COUNT(*)
-> FROM orders
-> WHERE orders.cust_id = customers.cust_id) AS orders
-> FROM customers
-> ORDER BY cust_name;
+----------------+------------+--------+
| cust_name | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc. | MI | 2 |
| E Fudd | IL | 1 |
| Mouse House | OH | 0 |
| Wascals | IN | 1 |
| Yosemite Place | AZ | 1 |
+----------------+------------+--------+
5 rows in set (0.00 sec)
2.2、联结表
外键:外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。
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 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)
2.2.1、WHERE 子句的重要性
在联结两个表的同时,你实际上做的是将第一个表中的每一行与第二个表中的每一行配对。WHERE 子句作为过滤条件,它只包含那些匹配给定条件的行。没有 WHERE 子句,第一个表中的每个行将与第二个表中的每个行配对,而不管它们逻辑上是否可以配在一起。
笛卡尔积:由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
mysql> SELECT vend_name,prod_name,prod_price
-> FROM vendors,products
-> ORDER BY vend_name,prod_name;
+----------------+----------------+------------+
| vend_name | prod_name | prod_price |
+----------------+----------------+------------+
| ACME | .5 ton anvil | 5.99 |
...
2.2.2、内部联结
目前为止所用的联结称为等值联结,它基于两个表之间的相等测试。这种联结称为内部联结。下面的语句返回与之前完全相同的数据:
mysql> SELECT vend_name,prod_name,prod_price
-> FROM vendors INNER JOIN products
-> ON vendors.vend_id = products.vend_id;
+-------------+----------------+------------+
| vend_name | prod_name | prod_price |
+-------------+----------------+------------+
| Anvils R Us | .5 ton anvil | 5.99 |
| Anvils R Us | 1 ton anvil | 9.99 |
| Anvils R Us | 2 ton anvil | 14.99 |
| LT Supplies | Fuses | 3.42 |
| LT Supplies | Oil can | 8.99 |
| ACME | Detonator | 13.00 |
| ACME | Bird seed | 10.00 |
| ACME | Carrots | 2.50 |
| ACME | Safe | 50.00 |
| ACME | Sling | 4.49 |
| ACME | TNT (1 stick) | 2.50 |
| ACME | TNT (5 sticks) | 10.00 |
| Jet Set | JetPack 1000 | 35.00 |
| Jet Set | JetPack 2000 | 55.00 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)
2.2.3、联结多个表
例如,显示编号为 20005 的订单中的物品:
mysql> SELECT prod_name,vend_name,prod_price,quantity
-> FROM orderitems,products,vendors
-> WHERE products.vend_id = vendors.vend_id
-> AND orderitems.prod_id = products.prod_id
-> AND order_num = 20005;
+----------------+-------------+------------+----------+
| prod_name | vend_name | prod_price | quantity |
+----------------+-------------+------------+----------+
| .5 ton anvil | Anvils R Us | 5.99 | 10 |
| 1 ton anvil | Anvils R Us | 9.99 | 3 |
| TNT (5 sticks) | ACME | 10.00 | 5 |
| Bird seed | ACME | 10.00 | 1 |
+----------------+-------------+------------+----------+
4 rows in set (0.00 sec)
2.3、创建高级联结
2.3.1、使用表别名
mysql> SELECT cust_name,cust_contact
-> FROM customers AS c,orders AS o,orderitems AS oi
-> WHERE c.cust_id = o.cust_id
-> AND oi.order_num = o.order_num
-> AND prod_id = 'TNT2';
+----------------+--------------+
| cust_name | cust_contact |
+----------------+--------------+
| Coyote Inc. | Y Lee |
| Yosemite Place | Y Sam |
+----------------+--------------+
2 rows in set (0.00 sec)
2.3.2、使用不同类型的联结
2.3.2.1、自联结
例如,想列出 ID 为 DTNTR 的物品供应商生产的其他物品:
mysql> SELECT p1.prod_id,p1.prod_name
-> FROM products AS p1,products AS p2
-> WHERE p1.vend_id = p2.vend_id
-> AND p2.prod_id = 'DTNTR';
+---------+----------------+
| prod_id | prod_name |
+---------+----------------+
| DTNTR | Detonator |
| FB | Bird seed |
| FC | Carrots |
| SAFE | Safe |
| SLING | Sling |
| TNT1 | TNT (1 stick) |
| TNT2 | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.00 sec)
2.3.2.2、自然联结
自然联结排除多次出现,使每个列只返回一次。自然联结是这样一种联结,其中你只能选择那些唯一的列。这一般是通过对表使用通配符(SELECT *),对所有其他表的列使用明确的子集来完成的:
mysql> SELECT c.*,o.order_num,o.order_date,oi.prod_id,oi.quantity,oi.item_price
-> FROM customers AS c,orders AS o,orderitems AS oi
-> WHERE c.cust_id = o.cust_id
-> AND oi.order_num = o.order_num
-> AND prod_id = 'FB';
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
| cust_id | cust_name | cust_address | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email | order_num | order_date | prod_id | quantity | item_price |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
| 10001 | Coyote Inc. | 200 Maple Lane | Detroit | MI | 44444 | USA | Y Lee | ylee@coyote.com | 20005 | 2005-09-01 00:00:00 | FB | 1 | 10.00 |
| 10001 | Coyote Inc. | 200 Maple Lane | Detroit | MI | 44444 | USA | Y Lee | ylee@coyote.com | 20009 | 2005-10-08 00:00:00 | FB | 1 | 10.00 |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
2 rows in set (0.00 sec)
2.3.2.3、外部联结
许多联结将一个表中的行与另一个表中的行相联结。但有时候会需要包含没有关联行的那些行。这种类型的联结称为外联结。
#检索所有用户,包括那些没有订单的客户
mysql> SELECT customers.cust_id,orders.order_num
-> FROM customers LEFT OUTER JOIN orders
-> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
| 10001 | 20005 |
| 10001 | 20009 |
| 10002 | NULL |
| 10003 | 20006 |
| 10004 | 20007 |
| 10005 | 20008 |
+---------+-----------+
6 rows in set (0.00 sec)
#使用内联结的情况
mysql> SELECT customers.cust_id,orders.order_num
-> FROM customers INNER JOIN orders
-> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
| 10001 | 20005 |
| 10001 | 20009 |
| 10003 | 20006 |
| 10004 | 20007 |
| 10005 | 20008 |
+---------+-----------+
5 rows in set (0.00 sec)
与内联结不同的是,外部联结还包括没有关联行的行。在使用 OUTER JOIN 时,必须使用 RIGHT 或 LEFT 关键字指出包括其所有行的表(RIGHT 指出的是 OUTER JOIN 右边的表,而 LEFT 指出的是OUTER JOIN 左边的表)。
2.3.2.4、使用带聚集函数的联结
#检索所有客户及每个客户所下的订单数
mysql> SELECT customers.cust_name,customers.cust_id,COUNT(orders.order_num) AS num_ord
-> FROM customers INNER JOIN orders
-> ON customers.cust_id = orders.cust_id
-> GROUP BY customers.cust_id;
+----------------+---------+---------+
| cust_name | cust_id | num_ord |
+----------------+---------+---------+
| Coyote Inc. | 10001 | 2 |
| Wascals | 10003 | 1 |
| Yosemite Place | 10004 | 1 |
| E Fudd | 10005 | 1 |
+----------------+---------+---------+
4 rows in set (0.00 sec)
2.4、组合查询
2.4.1、使用 UNION
#检索价格小于等于 5 的所有物品和包括供应商 1001 和 1002 生产的所有物品
mysql> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE prod_price <= 5
-> UNION
-> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE vend_id IN (1001,1002);
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
| 1003 | FC | 2.50 |
| 1002 | FU1 | 3.42 |
| 1003 | SLING | 4.49 |
| 1003 | TNT1 | 2.50 |
| 1001 | ANV01 | 5.99 |
| 1001 | ANV02 | 9.99 |
| 1001 | ANV03 | 14.99 |
| 1002 | OL1 | 8.99 |
+---------+---------+------------+
8 rows in set (0.00 sec)
UNION 的使用规则:
- UNION 必须使用两条或两条以上的 SELECT 的语句组成,语句之间使用 UNION 隔开;
- UNION 中的每个查询必须包含相同的列、表达式或聚集函数;
- 列数据类型必须兼容:类型不必完全相同,但必须是 DBMS 可以隐含地转换的类型。
2.4.2、包含或取消重复的行
#返回匹配的所有行(自动去掉了重复的行)
mysql> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE prod_price <= 5
-> UNION ALL
-> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE vend_id IN (1001,1002);
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
| 1003 | FC | 2.50 |
| 1002 | FU1 | 3.42 |
| 1003 | SLING | 4.49 |
| 1003 | TNT1 | 2.50 |
| 1001 | ANV01 | 5.99 |
| 1001 | ANV02 | 9.99 |
| 1001 | ANV03 | 14.99 |
| 1002 | FU1 | 3.42 |
| 1002 | OL1 | 8.99 |
+---------+---------+------------+
9 rows in set (0.00 sec)
2.4.3、对组合查询进行排序
mysql> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE prod_price <= 5
-> UNION ALL
-> SELECT vend_id,prod_id,prod_price
-> FROM products
-> WHERE vend_id IN (1001,1002)
-> ORDER BY vend_id,prod_price;
+---------+---------+------------+
| vend_id | prod_id | prod_price |
+---------+---------+------------+
| 1001 | ANV01 | 5.99 |
| 1001 | ANV02 | 9.99 |
| 1001 | ANV03 | 14.99 |
| 1002 | FU1 | 3.42 |
| 1002 | FU1 | 3.42 |
| 1002 | OL1 | 8.99 |
| 1003 | TNT1 | 2.50 |
| 1003 | FC | 2.50 |
| 1003 | SLING | 4.49 |
+---------+---------+------------+
9 rows in set (0.00 sec)
3、全文本搜索(待补充)
3.1、理解全文本搜索
并非所有的引擎都支持全文本搜索:两个常用的引擎 MyISAM 和 InnoDB。前者支持全文本搜索,而后者不支持。使用 LIKE 和正则表达式机制很有用,但它们仍然有几个重要的限制:
- 性能。通配符和正则表达式通常要求 MySQL 尝试匹配表中所有行。因此,由于被搜索行数不断增加,这些搜索可能很耗时;
- 明确控制。使用通配符和正则表达式很难明确地控制匹配什么和不匹配什么;
- 智能化的结果。
可以使用全文本搜索解决以上问题。
3.2、使用全文本搜索
3.2.1、启用全文本搜索支持
一般在创建表时启用全文本搜索。CREATE TABLE 语句接受 FULLTEXT 子句,它给出了被索引列的一个逗号分隔的列表。
mysql> CREATE TABLE productnotes(
-> note_id int NOT NULL AUTO_INCREMENT,
-> prod_id char(10) NOT NULL,
-> note_date datetime NOT NULL,
-> note_text text NULL,
-> PRIMARY KEY(note_id),
-> FULLTEXT(note_text))ENGINE=MyISAM;
为了进行全文本搜索,MySQL 根据子句 FULLTEXT(note_id) 的指示对它进行索引。在定义之后,MySQL 自动维护该索引。在增加、更新或删除行时,索引随之自动更新。
3.2.2、进行全文本搜索
在索引之后,使用两个函数 Match() 和 Against() 执行全文本搜索,其中 Match() 指定被搜索的列,Against() 指定要使用的搜索表达式。
mysql> SELECT note_text
-> FROM productnotes
-> WHERE Match(note_text) Against('rabbit');
+-----------------------------------------------------------------------------------------------------------------------+
| note_text |
+-----------------------------------------------------------------------------------------------------------------------+
| Customer complaint: rabbit has been able to detect trap, food apparently less effective now. |
| Quantity varies, sold by the sack load.
All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
+-----------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
传递给 Match() 的值必须与 FULLTEXT() 定义中的相同。如果指定多个列,则必须列出它们(而且次序正确)。搜索不区分大小写。全文本搜索的一个很重要的部分就是对比结果排序。具有较高等级的行先返回。