## 数据操作 - 查询
SELECT 大概是 SQL 语言中最常用的语句,而且怎样使用它也最为讲究;用它来选择记录可能相当复杂,可能会涉及许多表中列之间的比较。本节介绍 SELECT 语句关于查询的最基本功能。 语法如下:
SELECT column_list // 选择哪些列
FROM table_list // 从何处选择行
WHERE primary_constraint // 行必须满足什么条件
GROUP BY grouping_columns // 怎样对结果分组
HAVING secondary_constraint // 行必须满足的第二条件
ORDER BY sorting_columns // 怎样对结果排序
LIMIT count // 结果限定
上述语法中除了 SELECT 关键字之外,其他每样东西都是可选的。有些数据库需要用 FROM 关键字,但是 MySQL 允许对表达式求值而不引用任何表。
**注意:** 所有使用的关键词必须精确地以上面的顺序给出。例如,一个 HAVING 子句必须跟在 GROUP BY 子句之后和 ORDER BY 子句之前。
### 查询所有数据
SELECT * FROM employee; // 查所有数据
SELECT employee_name,job_title FROM employee; // 查特定列
使用星号(*)一些注意事项说明:
- 它会返回所有列数据,但是可能部分列数据我们并不使用,MySQL 数据库服务器和应用程序之间就会产生不必要的磁盘 I/O 和网络流量。
- 如果明确指定列,结果集更可预测,更易于管理。试想一下,当有其他开发人员修改表结构并添加更多的列,查询返回的结果集可能与您期望的就不一样了。
- 可能会暴露敏感信息给未经授权的用户。
### 条件查询
SELECT * FROM employee WHERE office_id = 1000; // 单个条件查询
SELECT * FROM employee WHERE office_id = 1000 AND gender = '男'; // 多条件 且关系 查询
SELECT * FROM employee WHERE office_id = 1000 OR office_id = 1001; // 多条件 或关系 查询
SELECT * FROM employee WHERE report_to IS NULL; // NULL 判断
SELECT * FROM employee WHERE report_to IS NOT NULL; // NOT NULL 判断
SELECT * FROM employee WHERE office_id IN (1000,1001); // 范围查询
SELECT * FROM employee WHERE office_id NOT IN (1000,1001); // 范围查询
SELECT * FROM employee WHERE office_id >= 1000 AND office_id <= 1005; // 范围查询
SELECT * FROM employee WHERE office_id BETWEEN 1001 AND 1005; // 范围查询
SELECT * FROM employee WHERE office_id NOT BETWEEN 1001 AND 1005; // 范围查询
### 模糊查询
SELECT * FROM employee WHERE employee_name LIKE '%吴'; // %通配符代表任意多个字符
SELECT * FROM employee WHERE employee_name LIKE '%吴%';
SELECT * FROM employee WHERE employee_name LIKE '吴_'; // _通配符代表任意一个字符
### 查询排序
SELECT * FROM employee ORDER BY office_id ASC; // 按照升序排列
SELECT * FROM employee ORDER BY office_id DESC // 按照降序排列
SELECT * FROM employee ORDER BY office_id, dept_id; // 按照两列进行排序,前面的为主要的
#### 分页查询
SELECT * FROM employee LIMIT 5; // 获取前5条数据
SELECT * FROM employee LIMIT 0,5; // 获取前5条数据
SELECT * FROM employee LIMIT 5,5; // 获取第5-10条数据
SELECT * FROM employee LIMIT 5 OFFSET 5; // 获取第5-10条数据
### 去重查询
SELECT DISTINCT status FROM `order`; // 查询有那些订单状态
### 统计函数(聚合函数)
SELECT COUNT(*) FROM `order`; // 查询表中有多少条数据
SELECT MAX(total_money) FROM `order`; // 取支付的最大值
SELECT MIN(total_money) FROM `order`; // 取支付的最小值
SELECT SUM(total_money) FROM `order`; // 取支付的总和
SELECT AVG(total_money) FROM `order`; // 取支付的平均值
**注意:** 如果表名或字段名是 MySQL 的保留字,需要用波浪号包裹起来,否则会引发错误。所以对表名以及字段命名请尽量避免 MySQL 保留字。
参考链接:[MySQL保留字](https://dev.mysql.com/doc/refman/5.7/en/keywords.html)
### 分组查询
SELECT status FROM `order` GROUP BY status; // 查询有那些订单状态
SELECT status, COUNT(*) AS total FROM `order` GROUP BY status; // 查询各类状态的订单有多少
SELECT status, COUNT(*) AS total FROM `order` GROUP BY status HAVING total > 50; 查询各类状态的订单量, 要求订单量大于50
**注意:** HAVING 子句筛选条件针对每一个分组的行,而 WHERE 子句的过滤条件是针对每个单独的行。
### 连接查询
**内连接(INNER JOIN)**
MySQL 的 INNER JOIN 子句的语法如下:
SELECT column_list
FROM t1
INNER JOIN t2 ON join_condition1
INNER JOIN t3 ON join_condition2
...
当我们使用 INNER JOIN 子句将 T1 表连接 T2 表:
在 T1 表的每一行与 T2 表中的每一行进行比较,检查是否它们都满足连接条件。当连接条件匹配,它将返回在 T1 和 T2 表合并选择列的行记录。
如果没有找到匹配,查询将返回一个空的结果集。
下面的思维图说明了 INNER JOIN 子句是如何工作的。结果集中的行必须出现在:T1 和 T2 两个表中。
![内连接原理图](http://www.studymysql.com/uploads/images/161007/07141053_99799.png)
从 product 表中读取产品代码(product_code)和产品名称(product_name)从 category 表中读取分类名称(name)
- product 表中的每个产品都属于 category 表中的一个分类;
- category 表中每个分类在 product 表中有 零个或多个产品;
- category 和 product 是一对多的关系;
```
SELECT
p.product_code,
p.product_name,
c.name AS category_name
FROM product AS p
INNER JOIN category AS c
ON p.category_id = c.category_id
LIMIT 10;
```
**左连接(LEFT JOIN)**
SELECT column_list
FROM t1
LEFT JOIN t2 ON join_condition1
LEFT JOIN t3 ON join_condition2
...
当我们使用 LEFT JOIN 子句将 T1 表连接 T2 表:
如果左表 T1 中的一行基于连接条件与右表 T2 一行相匹配,此行将被包括在结果集中。如果左表中的行不能匹配右表中的行,则左边的表中的行也被选择,并与右表中的一个“伪造”的行合并。这个”伪造”的行 用 NULL 值填充。
下面的思维图说明了 LEFT JOIN 子句是如何工作的。两个圆之间的交集表示那些符合两个表中的行记录,左圆的其余部分表示 T1 表没有与 T2 表相匹配的记录。左表中的所有行都会被包括在结果集中。
![左连接原理图](http://www.studymysql.com/uploads/images/161007/07211054_93390.png)
查找每个客户自己的所有订单:
- order 表中的每一个订单必须属于 customer 表中的某一客户;
- customer 表中每个客户在 order 表中有零个或多个订单;
- customer 和 order 是一对多的关系;
```
SELECT
cs.customer_id,
cs.customer_name,
o.order_number,
o.status
FROM `customer` AS cs
LEFT JOIN `order` AS o
ON cs.customer_id = o.customer_id
ORDER BY o.order_number ASC
LIMIT 10;
```
**右连接(RIGHT JOIN)**
右连接是相对于左连接,和左连接原理类似。把主表和连接表互换,也即是左连接和右链接互换。
```
SELECT
cs.customer_id,
cs.customer_name,
o.order_number,
o.status
FROM `order` AS o
RIGHT JOIN `customer` AS cs
ON o.customer_id = cs.customer_id
ORDER BY o.order_number ASC
LIMIT 10;
```
**全连接(FULL JOIN)**
只要其中某个表存在匹配,FULL JOIN 关键字就会返回行。
```
SELECT
cs.customer_id,
cs.customer_name,
o.order_number,
o.status
FROM `order` AS o
FULL JOIN `customer` AS cs
ON o.customer_id = cs.customer_id
ORDER BY o.order_number ASC
LIMIT 10;
```
**自连接(SELF JOIN)**
自连接是一个比较 “特殊” 的连接方式,它自己连接自已本身。
当我们想在同一个表中行记录上结合多列,可以考虑使用自连接。要执行自连接操作,必须要使用表的别名,以帮助 MySQL 从同一个表名称中区分左表和右表。
查找雇员以及雇员的直属领导:
在 employee 表中,我们不仅存储员工的数据,还有组织结构的数据。*report_to* 字段是用来存储这个雇员的管理者的ID。
为了获得整个组织结构,我们可以在 *employee_id* 和 *report_to* 这两个字段上连接 employee 表本身。
```
SELECT
e.office_id,
e.employee_name AS '雇员姓名',
m.employee_name AS '直属领导'
FROM employee AS e LEFT JOIN employee AS m
ON m.employee_id = e.report_to
ORDER BY e.office_id, m.employee_name;
```
### 子查询
MySQL 的子查询是嵌套在另一个查询中的查询,子查询也称为内部查询,那些包含子查询的查询被称为外部查询。
可以在任何地方,在一个表达式中使用子查询。此外,必须用括号将子查询包起来。
查找单笔支付额最大的支付信息:
```
SELECT * FROM payment WHERE total_money = (
SELECT MAX(total_money) FROM payment
);
```
查找所有在广州办公的雇员信息:
```
SELECT * FROM employee WHERE office_id IN (
SELECT office_id FROM office WHERE city = '广州'
);
```
查找所有下过订单的客户信息:
```
SELECT * FROM customer WHERE EXISTS (
SELECT * FROM `order` WHERE customer.customer_id = `order`.customer_id
);
```
查找所有未下过订单的客户信息:
```
SELECT * FROM customer WHERE NOT EXISTS (
SELECT * FROM `order` WHERE customer.customer_id = `order`.customer_id
);
```
exists 对外表逐条 loop 查询,每次查询都会查看 exists 的条件语句,当 exists 里的条件语句能够返回记录行时,返回当前 loop 到的这条记录,反之这条记录被丢弃。
### 合并查询
MySQL UNION 运算符允许从多个表查询出来的结果集组合成一个结果集。语法如下:
SELECT column1,column2
UNION[DISTINCT|ALL]
SELECT column1,column2
UNION[DISTINCT|ALL]
查找编号为 1001 的顾客 2015-2017 每年前 10 条订单信息:
```
SELECT order_id,customer_id,order_date FROM order_2015 WHERE customer_id = 1001 LIMIT 10
UNION ALL
SELECT order_id,customer_id,order_date FROM order_2016 WHERE customer_id = 1001 LIMIT 10
UNION ALL
SELECT order_id,customer_id,order_date FROM order_2017 WHERE customer_id = 1001 LIMIT 10
```
使用 UNION 的注意点:
- SELECT 语句列数量必须相等。
- SELECT 语句的列要有相同的数据类型,或至少是可转换的数据类型。
UNION DISTINCT 从结果集中清除重复行,UNION ALL 明确地保留重复行,UNION ALL 性能比 UNION DISTINCT 更好。
参考链接:
- [MySQL 子查询](http://www.studymysql.com/mysql/subquery.html)
- [MySQL UNION 用法](http://www.studymysql.com/mysql/union.html)
- [MySQL 中 EXISTS 与 IN 的使用](http://www.cnblogs.com/beijingstruggle/p/5885137.html)