学习目的
- 掌握Mysl常用的查询语句
- 掌握数据查询在实际开发中的应用
一.Mysql数据查询(DQL)
- DQL概念
DQL全称Data Query Language,意为数据查询语言。在数据库学习中,最重要的就是查询语句,因为数据库中的数据就是通过各种查询操作展示给用户。 - 特点
DQL最重要的语句是select语句。 - 语法格式
select 字段名1,字段名2,字段名3,.... from 表名; - 注意点
- 实际开发中不建议使用 select *,效率较低(原因是在底层会将*号先转换为所有字段再去查询,不如直接写出字段去查询);
- SQL语句中不区分大小写(Windows系统中不区分,但Linux中区分);
- 理清每一个查询语句的执行顺序,通过分步查询,有利于理解查询的执行原理以及调试。
1.1 简单查询
查询单个字段
select 字段名 from 表名;查询多个字段
select 字段名1,字段名2,字段名3,.... from 表名;查询某个字段 乘倍数之后的结果(字段可以参与数学运算)
select 字段名1,字段名2 *倍数, from 表名;给查询的某个字段运算后 重新命名(as关键字)
- 英文名:select 字段名1,字段名2 *倍数 as newName from 表名;
- 中文名格式:select 字段名1,字段名2 *倍数 as '中文名' from 表名;
mysql> select * from emp; //
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
mysql> select ename,sal as month,sal*12 as year from emp; //英文重命名
+--------+---------+----------+
| ename | month | year |
+--------+---------+----------+
mysql> select ename,sal as '月薪',sal*12 as '年薪' from emp; //中文重命名
+--------+---------+----------+
| ename | 月薪 | 年薪 |
+--------+---------+----------+
1.2 条件查询
- 语法格式
select
字段1,字段2,字段3...
from
表名
where
条件(字段名 = 字段数据);
- 执行顺序
- 先执行from:先明确从哪个位置(哪个表中)查询数据;
- 其次是where:明确查询的表后,需要确定查找的具体条件(一般是根据给定具体值的字段来查找);
- 最后是select:根据具体的条件"索引"到表中,查询满足条件的数据。
- 条件查询运算符
运算符 | 运算符描述 |
---|---|
= | 等于 |
<>或!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
between … and …. | 两个值之间,等同于 >= and <= |
is null | 为null(is not null 不为空) |
and | 并且 |
or | 或者 |
in | 包含,相当于多个or(not in不在这个范围中) |
not | 取非,主要用于is not null 或 not in中 |
like | 模糊查询,支持%或下划线匹配;%匹配任意个字符,一个下划线只匹配一个字符 |
- 注意点
- 查找某个区段之间的数据:可以使用大于小于<>号,也可以使用between and符号(闭区间),但是between and必须小范围在前,大范围在后;
- NULL:在数据库中的NULL并不是一个值(空值),只是一个标记代表该字段没有数据,因此判断一个字段是否为空不能使用 =号,而是使用运算符 is NULL 和 is not NULL;
1.2.1 and 和 or优先级
- 优先级
当and运算符和or运算符同时出现时,and优先级高于or优先级,但是对于多个运算符同时出现(且优先级不确定时),应该使用小括号()将每一部分的运算符括起来。 - 代码示例
mysql> select ename,sal,deptno from emp where sal > 1000 and deptno = 20 or deptno = 30;
+--------+---------+--------+
| ename | sal | deptno |
+--------+---------+--------+
| TURNER | 1500.00 | 30 |
| ADAMS | 1100.00 | 20 |
| JAMES | 950.00 | 30 |
| FORD | 3000.00 | 20 |
+--------+---------+--------+
4 rows in set (0.01 sec) //查找出 4条数据结果,有一个工资等于950 select ename,sal,deptno from emp where sal>1000 and (deptno=20 or deptno=30);
+--------+---------+--------+
| ename | sal | deptno |
+--------+---------+--------+
| TURNER | 1500.00 | 30 |
| ADAMS | 1100.00 | 20 |
| FORD | 3000.00 | 20 |
+--------+---------+--------+
3 rows in set (0.00 sec) //查找出 3条数据结果
1.2.2 in 运算符
- in 说明:in == 多个or
- in 使用格式
select 字段1,字段2
from 表名
where 条件字段 in(具体值1,具体值2);
//where 条件字段=具体值1 or 条件字段=具体值2;//上面in方式与or方式相等
- 特点
字段 in(具体值1,具体值2):括号内的具体值不是指数据在某个范围内,而是可以取 等于这两个不同的具体值; - not in:不在指定的具体几个值当中,和 in相反。
- 使用示例
select * from emp where job = 'SALESMAN' or job = 'MANAGER'; //or写法
select * from emp where job in('SALESMAN', 'MANAGER'); //in写法-等同于or
// in后面的值不是区间,是具体的值,表示取=800 or或 =5000的值
select ename,job from emp where sal in(800, 5000);
// not in,表示不取=1000 or或 =3000的值
select ename,job from emp where sal not in(1000, 3000);
1.2.3 like 模糊查询
- 说明
使用like模糊查询的原因是在条件查询中,并不知道完整的查找条件,可能只知道查找条件的一部分。但是为了能通过这一小部分也可以查询得到想要的数据结果,就可以使用like 表示查询条件中未知的一部分。 - like特定符
- %:百分号%代表任意多个字符,
- _:下划线_代表任意1个字符,但是指定了下标位置;
- % 与 _ 同时使用:当百分号% 与 下划线_ 同时使用时,对下划线必须使用转移字符 斜线\ 将下划线转换普通字符。
- 代码示例
//找出名字当中含有字母O的
select ename from emp where ename like '%O%';
//找出名字中第二个字母是A的
select ename from emp where ename like '_A%';
//找出名字中有下划线的
//select name from t_user where name like '%_%'; //错误写法,这样写是找出所有name
select name from t_user where name like '%\_%'; //正确写法
//找出名字中最后一个字母是T的
select ename from emp where ename like '%T';
1.3 排序数据
- 说明
对于查找得到的所有数据,希望得到一定顺序的输出结果,因此需要对查找的数据进行排序。 - 排序关键字:order by,默认升序排序。
- 语法格式
//无where子句
select 字段1,字段2,字段3...
from 表名
order by 字段(需是select中的查找字段)
//有where子句(单个字段排序)
select 字段1,字段2,字段3...
from 表名
where 条件(字段名 = 字段数据);
order by 字段(需是select中的查找字段)
//有where子句(多个字段排序)
select 字段1,字段2,字段3...
from 表名
where 条件(字段名 = 字段数据);
order by 字段1, 字段2 asc, 字段3 desc
//字段1先默认排序,再字段2 asc升序排序,最后字段3 desc降序排序
- 执行步骤
- 先执行from:先明确从哪个位置(哪个表中)查询数据;
- 其次是where:明确查询的表后,需要确定查找的具体条件(一般是根据给定具体值的字段来查找);
- 接着是select:根据具体的条件"索引"到表中,查询满足条件的数据;
- 最后是order by:对所有查找到的满足条件的结果 进行排序。(order by排序时可能没有where条件子句也是可行的) 。
- 特点
- 默认排序:没有指定排序规则时,order by默认为升序排序,也可以在order by字段后面加上 asc为升序排序,desc为降序排序;
- 多个字段排序:对于给查找的多个字段全部排序时,会先给写在最前面的字段先排序,依次给编写顺序的字段排序;
- 多个字段排序且指定排序顺序:给查找的多个字段排序时,可以通过在order by的每一个字段后面指定asc或desc对每一个字段进行排序;
- 多个字段排序主导性:给查找的多个字段排序时,编写顺序越靠前的字段起到主导作用,只有当前面的字段无法完成排序时,才会启用后面的字段排序。
- 代码示例
//查找所有员工的薪资并排序
select ename , sal from emp order by sal; // 默认升序
select ename , sal from emp order by sal asc; // asc升序
select ename , sal from emp order by sal desc; // desc降序
//按照员工工资的降序排列,当工资相同时按照员工名字升序排列
select ename,sal from emp order by sal desc; //先按照工资降序排列
select ename,sal from emp order by sal desc , ename asc; //在工资降序排列的基础上 对员工姓名升序排列
1.4 分组函数
说明
分组函数又称为聚合函数、统计函数、组函数,主要用于数据统计使用。分组函数可以方便获得一些特殊数据,如总数、最大值、最小值、平均值等。所有的分组函数都是对"某一组"数据进行操作的。函数关键字
- count(字段):计数,用于统计数据的个数(一行为一条数据),count(*)表示统计数据表的所有行数;
- sum(字段):求和,用于对某个字段的所有数据求总和;
- avg(字段):平均值,用于对某个字段的所有数据求平均数;
- max(字段):最大值,用于获取某一字段中的最大数;
- min(字段):最小值,用于获取某一字段中的最小数。
- 语法格式
//求总和
select sum(字段)
from 表名;
//求最大值
select max(字段)
from 表名;
//求最小值
select min(字段)
from 表名;
//求平均值
select avg(字段名)
from 表名;
//求数据的条数(不为NULL的记录条数)
select count(字段名)
from 表名;
//求表的总行数(总记录条数)
select count(*)
from 表名;
- 执行原理
- 先执行from:先明确从哪个位置(哪个表中)查询数据;
- 其次是where:明确查询的表后,需要确定查找的具体条件(一般是根据给定具体值的字段来查找);
- 最后是select:根据具体的条件"索引"到表中,查询满足条件的数据;
- 特点
- 出现位置:分组函数通常直接出现在select子句中,而不能直接出现在where子句中;
- 执行时间:分组函数一定是在分组查询group by进行分组之后才能执行;
- 所有的分组函数都是对"某一组" 或 "某一字段"数据进行操作的;
- 自动过滤null;所有的分组函数使用时会忽略没有数据的字段(即为null的字段),只统计有数据的字段(即使为0.0);
- 多行处理函数:分组函数又称多行处理函数,即输入多行,最终输出的结果只有1行。如sum(字段)是输入n行数据求和得出1行的结果,max(字段)是输入n行的数据得出1个/1行的最大数;
- 分组函数--分组查询联合使用:分组函数的使用一般都会联合分组查询一起,只有两个分组联合使用才体现出其意义;
- 所有数据库都有的规定:只要有NULL参与的运算,结果一定是NULL;而分组函数忽略掉所有NULL,因此NULL并没有参与分组函数的运算。
- 代码示例
//查询工资总和
select sum(sal) from emp;
//查询最高工资
select max(sal) from emp;
//查询最低工资
select min(sal) from emp;
//查询平均工资
select avg(sal) from emp;
//查询总人数
//select count(*) from emp; //count(*)表示统计所有行数/总行数
select count(ename) from emp;
- 注意点
分组函数不能直接在where子句当中使用(分组函数在group by分组之后才能执行,而where在group by之前执行)。
select 字段 from 表名 where 字段 > avg(字段);//错误提示(不合理语句)
select 字段 from 表名 where 字段 > (select avg(字段) from 表名);
1.4.1 单行处理函数
- 概念
- 多行处理函数:输入多行数据项 最终得出1行数据结果,分组函数都是多行处理函数;
- 单行处理函数:输入一行数据项 得出1行数据结果,输入多行数据项 得出多行数据结果。
- 特点
- 处理精细:对每一行输入数据及时处理,返回每一个对应的处理结果。
1.4.2 ifnull()
- 说明
ifnull()属于单行处理函数,是专门处理数据为NULL的空函数。由于NULL在参与数学运算时会将整个数学运算结果变为null,为了解决这种情况,因此使用ifnull()函数将可能为null的数据字段 变为数学0参与运算。 - 使用格式
ifnull(可能为NULL的字段,期望的处理值):将可能为NULL的字段数据 转换成 具体处理值的字段数据。 - 特点
- 将分组函数忽略NULL的特点 转变成 将NULL转换为实际有用的数据;
- 将 让数学运算无效的NULL 转换为 可以让数学运算正常运算的实际数据。
- 代码示例
//原始查询--可以为NULL
mysql> select ename,comm from emp;
+--------+---------+
| ename | comm |
+--------+---------+
| SMITH | NULL |
| ALLEN | 300.00 |
| WARD | 500.00 |
| JONES | NULL |
| TURNER | 0.00 |
//计算每个员工的年薪:年薪 = 工资sal + 补贴comm
//select ename,(sal+comm)*12 as yearsal from emp; //comm为NULL时,参与的运算结果一定是NULL
//使用ifnull()处理之后--NULL变为期望值0
mysql> select ename,ifnull(comm,0) as comm from emp;
+--------+---------+
| ename | comm |
+--------+---------+
| SMITH | 0.00 |
| ALLEN | 300.00 |
| WARD | 500.00 |
| JONES | 0.00 |
| TURNER | 0.00 |
//使用ifnull函数:comm为NULL 变成 comm为0
select ename,(sal+ifnull(comm,0))*12 as yearsal from emp;
1.5 分组查询
- 说明
分组查询不同于分组函数,分组函数针对的是对数据进行统计/计算处理,而分组查询是对字段的查询。 - 分组查询关键字
- group by:按照某个字段或者某些字段进行分组,group分组 + by通过分组的字段;
- having:对分组之后的数据进行再次过滤(效率较低,不如where子句过滤
)。
- 语法格式
5 select 字段1,字段2,字段3,分组函数(字段)...
1 from 表名
2 where 条件子句
3 group by 分组字段
4 having 在分组字段上的条件过滤
6 order by 排序字段
- 执行原理
- 首先是from:根据表名确定在哪个表中查找数据,确定表永远是第一步;
- 第二是where:根据具体的条件子句在已知表中进行条件过滤;
- 第三是group by:通过对字段进行分组处理,可以更加明确对分组的条件过滤;
- 第四是having:having是在group by分组之后加上的过滤条件(此过滤效率低下不如where子句过滤);
- 第五是select:以上的二到四都是条件过滤,从同时满足所有过滤条件的字段中 select查找对应字段名的数据(分组函数也在此时使用);
- 最后是order by:对查找得到的字段数据进行排序输出。
- 特点
- 分组函数--分组查询联合使用:分组函数的使用一般都会联合分组查询一起,只有两个分组联合使用才体现出其意义(分组查询与分组函数执行顺序前后的体现);
- 当一条sql语句中使用group by时,select后面只能跟分组函数 和 参与分组的字段(group by的字段);
- 当一条sql语句没有group by时,整张表的数据会自成一组。
- 代码示例
//自成一组:总共14个数据
mysql> select * from emp;
+-------+--------+-----------+------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
//分组查询:先根据job工种分组,再求每一个工种的平均薪资(5个工种5个数据)
mysql> select ename,job,avg(sal) from emp group by job;
+-------+-----------+-------------+
| ename | job | avg(sal) |
+-------+-----------+-------------+
| SCOTT | ANALYST | 3000.000000 |
| SMITH | CLERK | 1037.500000 |
| JONES | MANAGER | 2758.333333 |
| KING | PRESIDENT | 5000.000000 |
| ALLEN | SALESMAN | 1400.000000 |
+-------+-----------+-------------+
//分组查询:先根据sal工资分组,再求每一个工资的平均薪资(12种工资12个数据)
mysql> select ename,job,avg(sal) from emp group by sal;
+--------+-----------+-------------+
| ename | job | avg(sal) |
+--------+-----------+-------------+
| SMITH | CLERK | 800.000000 |
| JAMES | CLERK | 950.000000 |
| ADAMS | CLERK | 1100.000000 |
| WARD | SALESMAN | 1250.000000 |
| MILLER | CLERK | 1300.000000 |
| TURNER | SALESMAN | 1500.000000 |
| ALLEN | SALESMAN | 1600.000000 |
| CLARK | MANAGER | 2450.000000 |
| BLAKE | MANAGER | 2850.000000 |
| JONES | MANAGER | 2975.000000 |
| SCOTT | ANALYST | 3000.000000 |
| KING | PRESIDENT | 5000.000000 |
+--------+-----------+-------------+
//多个字段分组查询:先根据deptno部门编号分组,再根据工种job在部门分组基础上分组,最后从具体分好的组中查询平均薪资
mysql> select deptno,job,avg(sal) from emp group by deptno,job;
+--------+-----------+-------------+
| deptno | job | avg(sal) |
+--------+-----------+-------------+
| 10 | CLERK | 1300.000000 |
| 10 | MANAGER | 2450.000000 |
| 10 | PRESIDENT | 5000.000000 |
| 20 | ANALYST | 3000.000000 |
| 20 | CLERK | 950.000000 |
| 20 | MANAGER | 2975.000000 |
| 30 | CLERK | 950.000000 |
| 30 | MANAGER | 2850.000000 |
| 30 | SALESMAN | 1400.000000 |
+--------+-----------+-------------+
1.5.1 where 与 having
- 执行顺序
- where:where子句在整个sql语句的第二步开始执行,是整个sql中最先进行条件过滤的子句;
- having:having在对所有数据进行group by分组之后再执行,是在分组的基础上对分组的条件过滤,而group by在where之后执行。
- 执行特点
- where:对于求某单个数据,可以使用where进行提前过滤,再执行group by分组,效率较高;
- having: 对于求某个平均数据且涉及分组后的平均数,可以先进行group by分组再采用having过滤,因为使用where时会在分组前过滤掉一些数据(导致求平均值时不是最终的平均数据)。
- 代码示例
//找出每个部门的 最高薪资,要求显示薪资大于2900的数据
//第一步:找出每个部门的最高薪资,将部门分组,求每组部门最高
select max(sal),deptno from emp group by deptno;
//第二步:从每组部门最高,求大于2900的
//select max(sal),deptno from emp group by deptno having max(sal) > 2900; //分组后再过滤
select max(sal),deptno from emp where sal > 2900 group by deptno; //过滤小于2900后再分组
//找出每个部门的 平均薪资,要求显示薪资大于2000的数据
//第一步:找出每个部门的平均薪资,分组部分,后求平均工资
select deptno,avg(sal) from emp group by deptno;
//第二步:分组求平均数后,求大于2000的平均数(不能使用where,where在分组已过滤数据)
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2000;
1.5.2 distinct关键字(去重查询)
- 说明
distinct解释为去重复,在DQL查询中属于条件过滤的一种方式,是用来过滤去除一些重复的数据。distinct属于最后的过滤(distinct关键字在select后面出现,select是最后执行的条件过滤)。 - 使用格式
select distinct 字段1,字段2,...
from 表名
where 条件子句
...
- 特点
- distinct只能用于select子句,不能用于其他子句;
- distinct在select子句中只能出现在所有字段的最前面(表示distinct后面的字段全部去重);
- 一次过滤去重多个字段,但不能使用括号(),错误:select distinct(字段1,字段2);
- 一个select子句只能使用一个distinct关键字,错误:select distinct 字段1,distinct 字段2;
- 用于统计总类型的个数(一个类型可能有多个数据值,只求类型<种类>数)。
- 代码示例
// 去重:找出emp表中的工种(工种的个数不变,但每个工种有多个职员)
mysql> select distinct(job) from emp;
+-----------+
| job |
+-----------+
| CLERK |
| SALESMAN |
| MANAGER |
| ANALYST |
| PRESIDENT |
+-----------+
//mysql> select ename,distinct(job) from emp; //ERROR: distinct只能出现select子句的首位,所有字段的前面
mysql> select distinct(job),ename from emp;
//mysql> select distinct(job,ename) from emp; //ERROR: distinct每次只能去重一个字段
//mysql> select ename,job form emp where distinct(job); //ERROR: distinct只能出现在select子句,不能出现其他子句
//mysql> select distinct job,distinct deptno from emp; //ERROR :distinct一次只能出现一个
mysql> select distinct job,deptno from emp; //表示distinct后面的字段全部去重
// 统计岗位的数量:先去除重复的岗位,再统计岗位数量
select count(distinct job) from emp;
1.6 子查询
- 说明
子查询是指在一个DQL查询语句当中插入另外一个查询语句,具体是在本体的查询子句中 插入查询作为该子句的查询条件。子查询 即查询嵌套使用。 - 子查询关键字
- ()括号:将一个查询语句封装在括号中,将括号内查询返回的结果作为外部查询的条件;
- 语法格式
- select 子句子查询(极少使用,可读性差)
select
(select 字段 from 表1 where 条件)as 新字段 // 括号内子查询的返回结果可作为外部查询的字段
from
表2
where
条件子句;
- from子句子查询(实际开发最常使用)
select
别名2.字段
from
(select 字段 from 表1 where 条件)别名2 //括号内 子查询的返回结果作为新表,且另起别名
where
条件子句;
- where子句子查询
select
别名2.字段
from
表2 别名2
where
条件子句 字段1 >=<(select 字段 from 表1 where 条件) //括号内 子查询的返回结果作为条件子句的一部分条件
- 执行原理
- 先执行子查询中的语句
- 再回到外部查询的from 语句开始
- 特点
先执行子查询,并将子查询的返回结果作为外部查询的字段、临时表、条件等。 - 代码示例
1.6.1 表别名
- 概念
在DQL查询时,通过给每个数据表另起别名,可以让查询结构更加清晰易读,也避免多表查询时表名过多过长造成的冗余(可读性差)。 - 起名格式
select
a.字段,b.字段,c.字段
from
表1 别名a,表2 别名b,表3 别名c
where
条件子句(过滤条件)
- 使用特点
- 表别名必须在from子句中进行起名(因为所有的DQL查询语句第一步执行的就是from子句)
- 同一张表可以同时起两个别名,起两个别名后该两张表不是"同一张表"(自连接);
- 代码示例
// 原始查询
select emp.ename, dept.dname //"表名.字段"表达清晰,但字段太长易混乱
from emp, dept
where emp.deptno=dept.deptno;
// 使用别名查询
select e.ename, d.dname
from emp e, dept d //使用表别名,方便在其他字段使用
where e.deptno=d.deptno;
1.7 连接查询
说明
连接查询又叫跨表查询,需要关联多个表进行查询。在日常开发中极少要求进行单表查询,一般都是多个表联合进行查询(表与表之间存在联系<主键,外键>),不仅为了数据的独立性,也为了数据库的可维护性更好。连接查询版本
- SQL92:着重于使用where条件的连接查询,表连接过滤 和 查询过滤都在同一个where中使用多个 and进行拼接,可读性差,过滤条件冗余;
- SQL99:着重于使用join on表连接条件 + where数据查询条件 分离的查询,表连接过滤 和 查询过滤分离,层次更加清晰。
- 连接查询分类
- 内连接:包括等值连接(等号=)、非等值连接(between...and...)、自连接;
- 外连接:包括左外连接、右外连接;
- 全连接:
- 连接查询原理
- 多个数据表联合查询,区别于单表查询;
- 拿单表的一条数据(记录)和另外一张表的数据进行匹配,匹配的上则返回匹配的记录(有效数据);
- 连接查询实际原理:多表连接查询会产生笛卡尔积现象,通过条件筛选,过滤掉无效数据得到有效记录,就是多表查询。
1.7.1 笛卡尔积
- 概念
笛卡尔积又称笛卡尔乘积现象,指的是在数据库查询当中进行多表查询时,若未对多表联查进行条件过滤,则会出现多个重复且无效的数据,并且最终的查询结果集等于多个表的数据的个数乘积。 - 实际表示:笛卡尔积 = A表m条记录 X B表n条记录
- 处理方式
在查询中加条件进行过滤可以避免笛卡尔积现象(不会减少记录的匹配次数,只是显示有效记录)
1.7.2 内连接(inner join)
- 概念
内连接即多表查询中的单表内嵌,通过在一条查询语句中进行单表查询内嵌而形成多表查询的方式,成为内连接。假设A表和B表进行连接,凡是A表和B表能够匹配上的记录都查询出来--就是内连接。AB两张表没有主副之分,两张表是平等的。 - 实现方式
将一个查询拆分"粒度",将一个多表查询拆分成多个单表查询,尽量做到每一个字段出自于对应的表,然后使用inner join将单表查询连接,从而完成多表查询。 - 实现关键字:inner join、on
- 最大特点:只查询相等的数据(连接条件相等的数据)
- on关键字
on相当于where关键字,属于对查询的条件过滤,但是on着重于表的连接过滤,where是着重于对查询的条件过滤。
on经常与inner join同时搭配出现,表示在内连接过程中的条件过滤或筛选,实现表的连接条件和后来的where查询条件分离。 - 语法格式
// 两张表
select
a.字段1,b.字段2
from
表1 别名a
(inner)join
表2 别名b
on
a.字段 = b.字段; //表1和表2 连接的过滤条件
// 三张或以上多表
select
a.字段1,b.字段2 ,c.字段3
from
表1 别名a
(inner)join
表2 别名b
on
a.字段 = b.字段; //表1和表2 连接的过滤条件
(inner)join
表3 别名c
on
a.字段 = c.字段; //表1和表3 连接的过滤条件
//....下面可以继续 join on 或 left/right join on
where
a.字段 = b.字段; //查询的过滤条件
1.7.2.1 等值连接
- 概念
等值连接是内连接中,连接条件过滤的子句使用 等式= 作为过滤条件的连接。 - 最大特点
表的连接条件是等量关系(on连接部分是等量关系),A表的主键字段的值 = B表的外键字段的值,A表的主键必须作为B表的外键。 - 语法格式
// 等值连接--SQL92版
select
a.字段1,b.字段2
from
表1 别名a , 表2 别名b
where
a.字段 = b.字段; //查询的过滤条件
// 等值连接--SQL99版
select
a.字段1,b.字段2
from
表1 别名a
(inner)join
表2 别名b
on
a.字段 = b.字段; // 表连接的 过滤条件
where
a.字段 = b.字段; // 查询的 过滤条件
- 执行顺序/原理
1.7.2.2 非等值连接
- 概念
非等值连接是内连接中,连接条件过滤的子句使用 beetween and 等作为过滤条件的连接。 - 最大特点
表的连接条件是非等量关系(on连接部分是非等量关系)。 - 语法格式
// 非等值连接--SQL92版
select
a.字段1,b.字段2
from
表1 别名a , 表2 别名b
where
a.字段 between b.字段 and b.字段;
// 非等值连接--SQL99版
select
a.字段1,b.字段2
from
表1 别名a
(inner)join //inner可以省略
表2 别名b
on
a.字段 between b.字段 and b.字段; // 表连接的 过滤条件
where
a.字段 = b.字段; // 查询的 过滤条件
- 执行顺序/原理
1.7.2.3 自连接
- 概念
自连接指的是对于只有一张表,进行自己连接自己的查询,将一张表使用两个"别名"来区分成两张表,进而可以筛选过滤。 - 最大特点
一张表看做两张表,自己连接自己,自己查自己。 - 语法格式
// 自连接:本身是同一张表,使用不同别名作为两张表
select
a.字段1 as '新字段名',b.字段2 as '新字段名'
from
表1 别名a
(inner)join // 表1 连接 表1
表1 别名b
on
a.字段1 = b.字段2; //"两张表"的字段采用等值连接
- 执行顺序/原理
- 先对原表起别名
1.7.3 外连接
- 概念
假设A表和B表进行连接,外连接时A、B两张表中有一张表是主表,一张表是副表,主要查询主表中的数据,捎带着查询副表。当副表中的数据没有和主表中的数据匹配上,副表自动模拟出NULL与之匹配。 - 最大特点
- 主表的数据无条件的全部查询出来,内连接(等值连接)则指查找出字段值相等的数据,当字段值为空或者不相等则不查出;
- 外连接查询出来的数据会更加完整,内连接会丢失数据;
- outer关键字可以省略;
- 笔试最后一题一般是外连接查询。
- 分类
- 左外连接:以左边的表为主,无条件查询出左边表的所有数据记录;
- 右外连接:以右边的表为主,无条件查询出右边表的所有数据记录;
- 左外连接与右外连接的转换
- 每一个左外连接都必定有一个右外连接的写法;
// 左外连接
from
表1 a
left outer join // outer可以省略
表2 b
on
a.字段 = b.字段; // 表连接的 过滤条件
// 右外连接
1.7.3.1 左外连接(left join)
- 概念
以左边的表为主,连接右边的表一起查询,连接查询的实际原理就是以一张表的数据记录 去匹配另一张表的数据记录。但左外连接使表的连接顺序拥有左边优先级。 - 最大特点
左边的表是主表,获取左表所有记录,即使右表没有对应匹配的记录。 - 语法格式
// 左外连接:以左表为主
select
a.字段1 as '新字段1', b.字段2 as '新字段2' //as可以省略
from
表1 a
left outer join // outer可以省略
表2 b
on
a.字段 = b.字段; // 表连接的 过滤条件
where
a.字段 = b.字段; // 数据查询的 过滤条件
- 执行顺序/原理
1.7.3.2 右外连接(right join)
- 概念
以右边的表为主,连接左边的表一起查询,连接查询的实际原理就是以一张表的数据记录 去匹配另一张表的数据记录。但右外连接使表的连接顺序拥有右边优先级。 - 最大特点
右边的表是主表,用于获取右表所有记录,即使左表没有对应匹配的记录。 - 语法格式
// 右外连接:以右表为主
select
a.字段1 as '新字段1', b.字段2 as '新字段2' //as可以省略
from
表1 a
right outer join // outer是可以省略
表2 b
on
a.字段 = b.字段;
- 执行顺序/原理
1.7.4 全连接(full join)
- 概念
假设A表和B表进行连接,外连接时A、B两张表都是主表,没有副表。查询时将A、B表的数据都无差别查询出来。 - 最大特点
每一张表都是主表,没有副表。 - 语法格式
- 执行顺序/原理
1.8 union关键字
- 说明
对于多表查询或者多个字段查询,union关键字是将查询拆分为多个单一的字段查询,然后将多个单一查询的结果集进行相加,返回一个查询结果的合集。 - 最大特点
- 将两张或多张毫无相关的表的查询结果连接输出;
- 作用可以取代or 或 in 关键字的部分用法;
- union相加只能是每个查询是查询相同的列数或字段数,列数不同或查询字段数不同的多个查询不能想加;
- 查询结果输出有序(union实则为分步查询,有先后)。
- 语法格式
// 同表查询
select 字段1,字段2 from 表1 where 字段 = '数据值1'
union
select 字段1,字段2 from 表1 where 字段 = '数据值2'
// 多表查询--可以将不同表的查询结果 相加结合输出
select 字段1,字段2 from 表1 where 字段1 = '数据值'
union
select 字段3,字段4 from 表2 where 字段3 = '数据值'
- 执行原理 /顺序
将一个总的查询分为多个查询小分支,每一个小分支都可以查询不同的表,最后使用union关键字将每一个小分支的查询结果返回作为整个总-查询的结果。 - 代码示例
- 单表 union相加字段
// 要求:找出工作岗位是SALESMAN和MANAGER的员工
// 使用 or关键字
select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
// 使用 in关键字
select ename,job from emp where job in('MANAGER','SALESMAN');
// 输出结果无序,查找字段断续间接输出
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | SALESMAN |
+--------+----------+
// 使用 union关键字
select ename,job from emp where job = 'MANAGER' // 第一步:先查一个字段
union // 第三步:将结果相加
select ename,job from emp where job = 'SALESMAN'; //第二步:查另外一个字段
// 查询结果相加输出--字段有序输出(原因是查询分步)
+--------+----------+
| ename | job |
+--------+----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| MARTIN | SALESMAN |
| TURNER | SALESMAN |
+--------+----------+
- 多表 uinion相加
// 查询所有员工和部门名称
mysql> select ename from emp // 第一步:从emp表查询所有员工
-> union // 第三步:将两个查询结果相加
-> select dname from dept; //第二步:从dept表查询所有部门
+------------+
| ename |
+------------+
| SMITH |
| ALLEN |
| WARD |
| JONES |
| MARTIN |
| BLAKE |
| CLARK |
| SCOTT |
| KING |
| TURNER |
| ADAMS |
| JAMES |
| FORD |
| MILLER |
| ACCOUNTING |
| RESEARCH |
| SALES |
| OPERATIONS |
+------------+
1.9 limit关键字
- 说明
limit关键字是Mysql中特有的关键字,其他数据库中没有,不能通用(Oracle中有一个相同的机制--rownum)。 - 最大特点
- limit可以实现分页查询;
- limit是取出查询结果集中的一部分数据;
- limit是整个SQL语句中最后执行的一步。
- 语法格式
// startIndex表示起始位置,从下标0开始表示第一条数据
// length表示取出结果集中的 几个
limit startIndex, length
// 省略startIndex起始位置 时 ,默认从下标0开始取length个数据
limit length;
执行顺序
5 select
字段1,字段2,...
1 from
表1 别名a...
/*(inner/left/right join
表2 别名b
on
表连接条件) // 表连接查询可有可无,看具体业务
*/
2 where
查询过滤条件
3 group by
分组字段
4 having
分组的过滤字段
6 order by
排序
7 limit
起始位置,获取长度/个数;
- 执行原理
在其他所有SQL子句执行结束之后,limit才会执行。limit是整个SQL中最后执行的一步,在select子句执行之后获得了查询结果在执行,是对查询结果的过滤。 - 代码示例
// emp表中的员工与薪资数据--按照降序输出
mysql> select ename,sal from emp order by sal desc;
+--------+---------+
| ename | sal |
+--------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| MARTIN | 1250.00 |
| WARD | 1250.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| SMITH | 800.00 |
+--------+---------+
// 取出工资排前5名的员工--先对薪资排序
select ename,sal from emp order by sal desc;
// 从排序的薪资中取前5个
select ename,sal from emp order by sal desc limit 0, 5; // 从起始下标0开始,取5个
select ename,sal from emp order by sal desc limit 5; // 只有一个数字表示取得长度,默认从下标0开始
// 找出工资排名在第4到第9名的员工
select ename,sal from emp order by sal desc limit 3,6; //下标从0开始
1.9.1 通用分页SQL
- 分页原理
每页显示3条记录:起始页,每页显示的数据条数
第1页:0, 3
第2页:3, 3
第3页:6, 3
第4页:9, 3
第5页:12, 3 - 分页实现关键
每页显示pageSize条记录;
第pageNo页:页码(pageNo - 1) * pageSize,数据条数pageSize
- pageSize:每页显示的多少条记录
- pageNo:显示的第几页
- java实现分页
java分页代码 {
int pageNo = 2; // 第几页:页码是2
int pageSize = 10; // 每页显示的数据:10条
String sql = limit (pageNo - 1) * pageSize, pageSize;// 分页显示的页码 和当前页码显示的数据条数
}
常见面试题
提供的参考表
dept表
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
emp表
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
salgrade表
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+