MySQL基础学习笔记之——SELECT查询

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 的所有客户:

  1. 检索包含物品 TNT2 的所有订单的编号;
  2. 检索具有前一步骤列出的订单编号的所有客户 ID;
  3. 检索前一步返回的所有客户 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 表中每个客户的订单总数:

  1. 从 customers 表中检索客户列表;
  2. 对于检索出的每个客户,统计其在 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() 定义中的相同。如果指定多个列,则必须列出它们(而且次序正确)。搜索不区分大小写全文本搜索的一个很重要的部分就是对比结果排序。具有较高等级的行先返回。

猜你喜欢

转载自blog.csdn.net/qq_36879493/article/details/107896098