MySQL(02)—数据处理之查询
数据查询
使用前,对当前数据库写入测试用的数据库脚本,点击下载脚本
1. 基本的SELECT语句
1 | SELECT * | {[DISTINCT] cloumn | expression [alias],...} |
SELECT 标识选择哪些列
FROM 标识从哪个表中选择
选择全部列
1
2
3use myemployees;
select *
from departments;
选择特定列
1
2select department_id, location_id
from departments;
使用别名
重命名一个列。 便于计算。紧跟列名,也可以在列名和别名之间加入关键字 ‘AS’,别名使用双引号,以便在别名中包含空 格或特殊的字符并区分大小写
1
2select last_name as name, commission_pct comm
from employees;
1
2SELECT last_name "Name", salary*12 "Annual Salary"
FROM employees;
字符串
字符串可以是 SELECT 列表中的一个字符,数字,日期。
日期和字符只能在单引号中出现。
每当返回一行时,字符串被输出一次
显示表结构
1 | DESCRIBE employees; |

2. 过滤和排序数据
2.1 过滤
使用where子句,将不满足条件的行过滤掉
1 | SELECT *|{[DISTINCT] column|expression [alias],...} |
WHERE 子句紧随 FROM 子句
返回在90号部门工作的所有员工的信息
1 | select employee_id,last_name,job_id,department_id |

2.1.1 比较运算符
| 操作符 | 含义 |
|---|---|
| = | 等于 (不是 ==) |
| > | 大于 |
| >= | 大于、等于 |
| < | 小于 |
| <= | 小于等于 |
| <> | 不等于(也可以是!=) |
查询薪资在3000以下的员工的姓氏和薪资
1 | SELECT last_name, salary |
2.1.2 1其他比较运算符
| 操作符 | 含义 |
|---|---|
| BETWEEN …AND… | 在两个值之间 (包含边界) |
| IN(set) | 等于值列表中的一个 |
| LIKE | 模糊查询 |
| IS NULL | 空值 |
使用BETWEEN AND运算来显示在一个区间内的值
1
2
3select last_name,salary
from employees
where salary between 2500 and 3500;

使用IN运算显示列表中的值
1
2
3select employee_id,last_name,salary,manager_id
from employees
where manager_id in (100,101,201);
使用LIKE运算选择类似的值
选择条件可以包含字符或数字:
- %代表零个或多个字符(任意个字符)
- _代表一个字符
1
2
3select first_name
from employees
where first_name like 'S%';
使用IS(NOT)NULL判断空值
1
2
3select last_name,manager_id
from employees
where manager_id is null;
2.1.3 逻辑运算
| 操作符 | 含义 |
|---|---|
| AND | 逻辑与 |
| OR | 逻辑或 |
| NOT | 逻辑否 |
AND要求并的关系为真
1
2
3
4select employee_id,last_name,job_id,salary
from employees
where salary >= 1000
and job_id like '%MAN%';
OR要求或关系为真
1
2
3
4select employee_id,last_name,job_id,salary
from employees
where salary >= 10000
or job_id like '%MAN%';
NOT
1
2
3
4select last_name,job_id
from employees
where job_id
not in ('IT_PROG','ST_CLERK','SA_REP');
2.1.4 ORDER BY子句
使用ORDER BY子句排序
- ASC(ascend):升序
- DESC(descend):降序
- ORDER BY子句在SELECT语句的结尾
默认使用升序排列
1
2
3select last_name,job_id,department_id,heirdate
from employees
order by hiredate;
使用降序排列
1
2
3select last_name,job_id,department_id,hiredate
from employees
order by hiredate desc;
按别名排序
1
2
3select employee_id,last_name,salary*12 'annual salary'
from employees
order by 'annual salary';
按照ORDER BY列表的顺序排序
1
2
3select last_name,department_id,salary
from employees
order by department_id,salary desc;
可以使用不在SELECT列表里中的列排序
3. 分组函数
3.1 什么是分组函数
分组函数作用于一组数据,并对一组数据返回一个值

3.2 组函数类型
- AVG()
- COUNT()
- MAX()
- MIN()
- SUM()
3.3 组函数语法
1 | SELECT [column,] group_function(column), ... |
AVG(平均值)、MIN(最小值)、 MAX(最大值)、SUM(合计)函数
1
2
3select avg(salary),max(salary),min(salary),sum(salary)
from employees
where job_id like '%REP%';
COUNT(*)返回表中记录总数,适用于任意数据类型
1
2
3select count(*)
from employees
where department_id = 50;
COUNT(expr)返回expr不为空的记录总数
1
2
3select last_name,count(commission_pct)
from employees
where department_id = 50;
3.4 分组数据
3.4.1 单列分组

GROUP BY子句语法,可以使用GROUP BY子句将表中的数据分成若干组
1 | SELECT column, group_function(column) |
明确:WHERE一定放在FROM后面
在SELECT 列表中所有未包含在组函数中的列都应该包含 在 GROUP BY 子句中。
1
2
3select department_id,AVG(salary)
from employees
group by department_id;
包含在GROUP BY子句中的列不必包含在SELECT列表中
1
2
3select avg(salary)
from employees
group by department_id;
3.4.2 多列分组

在GROUP BY子句中包含多个列
1
2
3select department_id,dept_id,job_id,sum(salary)
from employees
group by department_id,job_id;
3.4.3 过滤分组

使用HAVING过滤分组
行已经被分组
使用了组函数
满足HAVING子句中条件的分组将被显示
1
2
3
4
5
6SELECT column, group_function
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[HAVING group_condition]
[ORDER BY column];HAVING子句
1
2
3
4select department_id,MAX(salary)
from employees
group by department_id
having max(salary)>10000;
3.4.4 SELECT语句执行顺序
having子句与where都是设定条件筛选的语句,有相似之处也有区别。
having与where的区别:
having是在分组后对数据进行过滤
where是在分组前对数据进行过滤
having后面可以使用聚合函数
where后面不可以使用聚合
在查询过程中执行顺序:from>where>group(含聚合)>having>order>select。
所以聚合语句(sum,min,max,avg,count)要比having子句优先执行,而where子句在查询过程中执行优先级别优先于聚合语句(sum,min,max,avg,count)。
where子句:
select sum(num) as rmb from order where id>10
//只有先查询出id大于10的记录才能进行聚合语句
having子句:
select reports, count() from employees group by reports having count() > 4
上例having条件表达示为聚合语句,肯定的说having子句查询过程执行优先级别低于聚合语句。
再换句说话说把上面的having换成where则会出错,统计分组数据时用到聚合语句。
对分组数据再次判断时要用having。如果不用这些关系就不存在使用having。直接使用where就行了。
having就是来弥补where在分组数据判断时的不足。因为where执行优先级别要快于聚合语句。
聚合函数:
例如SUM, COUNT, MAX, AVG等。这些函数和其它函数的根本区别就是它们一般作用在多条记录上。
HAVING子句可以让我们直接筛选成组后的各组数据,也可以在聚合后对组记录进行筛选,而WHERE子句在聚合前先筛选记录,也就是说作用在GROUP BY 子句和HAVING子句前。
3.4.5 非法使用组函数
不能在WHERE子句中使用组函数
可以在HAVING子句中使用组函数
1 | SELECT department_id, AVG(salary) |
1 | WHERE AVG(salary) > 8000 |
4. 多表查询
两表做笛卡尔积
4.1 多表连接
语法:
1 | select name,boyName |
4.2 笛卡尔集
笛卡尔集会在下面的条件下产生
①省略连接条件;②连接条件无效;③所有表中的所有行互相链接
为了避免笛卡尔集,可以在WHERE加入有效的连接条件
4.3 MySQL连接
使用连接在多个表中查询数据
1 | SELECT table1.column, table2.column |
在 WHERE 子句中写入连接条件。 在表中有相同列时,在列名之前加上表名前缀
等值连接
1
2
3select beauty.id,name,boyName
from beauty,boys
where beauty.boyfriend_id=boys.id;
区分重复的列名:使用表名前缀在多个表中区分相同的列,在不同表中具有相同列名的列可以用表的别名加以区分,如果使用了表别名,则在select语句中需要使用表别名代替表名,表别名最多支持32个字符长度,但建议越少越好
表的别名
1
2
3select bt.id,name,boyName
from beauty bt, boys b
where bt.boyfriend_id=b.id;
4.4 连接多个表

连接 n个表,至少需要 n-1个连接条件。 例如:连接 三个表,至少需要两个连接条件。
查询出公司员工的 last_name, department_name, city
1
2
3
4select last_name,department_name,city
from employees,departments,locations
where employees.department_id = departments.department_id
and departments.location_id = locations.location_id;
4.5 使用ON子句创建连接
自然连接中是以具有相同名字的列为连接条件的,可以使用ON子句指定额外的连接条件,这个连接条件是与其他条件分开的,ON子句使语句具有更高的易读性
4.5.1 JOIN连接
分类:
内连接 [inner] join on
(典型的连接运算,使用像 = 或 <> 之类的比较运算符)。包括相等联接和自然联接。内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如,检索 students和courses表中学生标识号相同的所有行。
外连接
外联接可以是左向外连接、右向连接或完整外部连接。在 FROM子句中指定外联接时,可以由下列几组关键字中的一组指定:
LEFT JOIN或LEFT OUTER JOIN
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
RIGHT JOIN 或 RIGHT OUTER JOIN
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
FULL JOIN 或 FULL OUTER JOIN
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
交叉联接
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
FROM 子句中的表或视图可通过内联接或完整外部联接按任意顺序指定;但是,用左或右向外联接指定表或视图时,表或视图的顺序很重要。有关使用左或右向外联接排列表的更多信息,请参见使用外联接。
示例
内连接
1
2select bt.*,boys.*
from beauty bt inner join boys on bt.boyfriend_id = boys.id;
左连接
1
2select bt.*,boys.*
from beauty bt left join boys on bt.boyfriend_id = boys.id;
左表独有
1
2
3select bt.*,boys.*
from beauty bt left join boys on bt.boyfriend_id = boys.id
where boys.id is null;
右连接
1
2select bt.*,boys.*
from beauty bt right join boys on bt.boyfriend_id = boys.id;
右表独有
1
2
3select bt.*,boys.*
from beauty bt right join boys on bt.boyfriend_id = boys.id
where bt.boyfriend_id is null;
完全连接
oracle里面有full join,但是在mysql中没有full join。我们可以使用union来达到目的。
1
2
3select bt.* from beauty bt left join boys on bt.boyfriend_id = boys.id
union
select bt.* from beauty bt right join boys on bt.boyfriend_id = boys.id;
并集去交
1
2
3
4
5select * from beauty bt left join boys on bt.boyfriend_id = boys.id
where boys.id is null
union
select * from beauty bt right join boys on bt.boyfriend_id = boys.id
where bt.boyfriend_id is null;
5. SELECT语句执行顺序
查询操作是关系数据库中使用最为频繁的操作,也是构成其他SQL语句(如DELETE、UPDATE)的基础。我们知道,SQL 查询的大致语法结构如下:
1 | (5)SELECT DISTINCT <select_list> |
这些步骤执行时,每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只是最后一步生成的表才会返回给调用者。如果没有在查询中指定某一子句,将跳过相应的步骤。
SELECT各个阶段分别干了什么:
5.1 FROM阶段
FROM阶段标识出查询的来源表,并处理表运算符。在涉及到联接运算的查询中(各种JOIN),主要有以下几个步骤:
- 求笛卡尔积。不论是什么类型的联接运算,首先都是执行交叉连接(CROSS JOIN),求笛卡儿积(Cartesian product),生成虚拟表VT1-J1。
- ON筛选器。 这个阶段对上个步骤生成的VT1-J1进行筛选,根据ON子句中出现的谓词进行筛选,让谓词取值为true的行通过了考验,插入到VT1-J2。
- 添加外部行。如果指定了OUTER JOIN,如LEFT OUTERJOIN、RIGHT OUTER JOIN,还需要将VT1-J2中没有找到匹配的行,作为外部行添加到VT1-J2中,生成VT1-J3。如果FROM子句包含两个以上表,则对上一个连接生成的结果表VT1-J3和下一个表重复依次执行3个步骤,直到处理完所有的表为止。
经过以上步骤,FROM阶段就完成了。
5.2 WHERE阶段
WHERE阶段是根据
5.3 GROUP BY阶段
GROUP阶段按照指定的列名列表,将VT2中的行进行分组,生成VT3。最后每个分组只有一行。在GROUP BY阶段,数据库认为两个NULL值是相等的,因此会将NULL值分到同一个分组中。
5.4 HAVING阶段
该阶段根据HAVING子句中出现的谓词对VT3的分组进行筛选,并将符合条件的组插入到VT4中。COUNT(expr) 会返回expr不为NULL的行数,count(1)、count(*)会返回包括NULL值在内的所有数量。
5.5 SELECT阶段
这个阶段是投影的过程,处理SELECT子句提到的元素,产生VT5。这个步骤一般按下列顺序进行:
- 计算SELECT列表中的表达式,生成VT5-1。
- 若有DISTINCT,则删除VT5-1中的重复行,生成VT5-2。
5.6 ORDER BY阶段
根据ORDER BY子句中指定的列明列表,对VT5-2中的行,进行排序,生成VT6。如果不指定排序,数据并非总是按照主键顺序进行排序的。NULL被视为最小值。
5.7 LIMIT阶段
取出指定行的记录,产生虚拟表VT7,并返回给查询用户。LIMIT n, m的效率是十分低的,一般可以通过在WHERE条件中指定范围来优化 \WHERE** id > ? limit 10。**

