MySQL基础语句三(多表查询)
多表设计的两种方式:
主键:
能够唯一表示数据表中的每个记录的字段或者字段的组合就称为主键。
外键:
若有两个表A和B,C是A的主键,而B中也有C字段,则C就是表B的外键,外键约束主要用来维护两个表之间数据的一致性。
A为基本表,B为信息表。
id和外键的优缺点:
-
外键:
①优点:- 数据一致性,比如插入了某个员工,但是这个员工的外键 部门id对应的记录并不存在,数据插入不成功。
- 数据删除之后的操作,比如我们可以设置当某个部门信息被删除后,对应部门下的员工信息等是否应该删除,还是设置为null,这些操作我们事先设置好以后,写代码的时候就不用去管了,要不然我们就得自己写代码去完成。
- 上述优点可以保证数据的可靠性。
②缺点:
会影响性能。数据库为了保证数据的完整性会使得并发性降低。 -
id方式:
①优点:- 灵活
- 并发性高
②缺点:
- 数据完整性很难保证,需要自己写代码入库的时候确保好数据完整性。
- 会让写代码的人量加大。
如何选择:
- 数据一致性必须得到保证,这点非常重要
- 如果并发量要求不高,优先选择外键,因为外键会让你写代码的时候会少很多工作量,因为一致性你必须考虑到,让写代码的人去确保数据一致性,如果水平参差不齐容易出bug
- 数据并发量要求高就用id的方式。这点没什么可犹豫的
- 数据量大的情况下不要用外键,比如上百万、千万等。
- 数据库的性能没有我们想象的那么低,所以几十万的数据量并不大。
1、并集:union
UNION用于把来自许多SELECT语句的结果组合到一个结果集合中,也叫联合查询。SELECT ... UNION [ALL | DISTINCT] SELECT ... [UNION [ALL | DISTINCT] SELECT ...]
当使用 UNION 时,MySQL 会把结果集中重复的记录删掉
而使用 UNION ALL ,MySQL 会把所有的记录返回,且效率高于 UNION
2、内连接:inner
表的连接分为:
- 内连接(inner join)
- 外连接(outter join)
- 自然连接
- 全连接
- union
内连接:
select field1,...fieldn from table1 inner join table2 [inner join tablen] on join_condition;
inner可以省略
内连接就是在表关系的笛卡尔积数据记录中,保留关系中所有匹配的数据记录,舍弃不匹配的数据记录
加on语句:select * from employee join dept ON employee.deptno=dept.deptno;
可以使用and,or将多个条件连接起来
使用using简化上述用法:select * from employee join dept using(deptno);
on语句更加灵活,因为两个字段在两张表中不一定要同名.
三张表连接:select e.no, e.name, e.sal, e.job, l.name, l.leader, d.name, d.loc from employee e inner join employee l on e.mgr=l.no inner join dept d on l.deptno=d.deptno;
3、外连接:
select field1,...fieldn from table [left|right] [outer] join table2 on join_condition;
-
左连接:left join
select * from employee LEFT JOIN dept using(deptno);
-
右连接: right join
select * from employee f RIGHT OUTER JOIN employee j on f.mgr=j.no;
select * from employee RIGHT JOIN dept using(deptno);
左连接:以 LEFT JOIN 左侧也就是前面的表作为主表,返回所有左侧表中的记录,右侧表中不存在匹配时在相应字段不NULL。
右连接:以 RIGHT JOIN 右侧也就是后面的表作为主表,返回所有右侧表中的记录,左侧表中不存在匹配时在相应字段不NULL。
4、子查询:
- 当一个查询是另一个查询的条件时,称之为子查询。
- 子查询最常用于SELECT-SQL命令的WHERE子句中。
- 子查询是一个 SELECT 语句,它嵌套在一个 SELECT、SELECT…INTO 语句、INSERT…INTO 语句、DELETE 语句、或 UPDATE 语句或嵌套在另一子查询中。
- 我们可以通过子查询来解决,所谓子查询就是:
一个查询中嵌套了其他若干查询,即在一个select查询语句的where或者from子句中包含另一个select查询语句。这个语句中外层的select查询语句称为主查询,where子句中的select查询语句被称为子查询。
通过子查询可以实现多表查询,该查询语句可能包括in、any、all和exists等关键字。除此之外还可能包含比较运算符。
where子句子查询: 该位置的子查询一般返回单行单列、多行单列、单行多列数据
from子句的子查询: 该位置处的子查询一般返回多行多列数据,可以当做一张临时表
返回单行单列和单行多列子查询:
当子查询返回的结果为单行单列数据记录时,该子查询语句一般在主查询语句的where子句里,通过会包含比较运算符: >、<、=、 !=
单行单列:
查询雇员表中工资比smith还要高的全部雇员信息
第一步:查询smith的薪资select sal from employee where name='smith';
第二步:查询比smith薪资高的员工信息select * from employee where sal>( select sal from employee where name="smith" )
单行多列:
查询雇员中工资和职位与smith一样的全部雇员信息
第一步:查询smith的工资和职位select sal, job from employee where name="smith";
第二步:select name, sal, job from employee where (sal,job) = ( select sal, job from employee where name="smith" )
返回多行单列子查询:
当子查询中返回的结果为多行单列,该子查询一般会在主查询语句的where子句中出现,通常包含in、any、all、exists等关键字。
-
带in关键字
当主查询的条件是子查询结果中时,就可以通过关键字in来进行判断,相反如果想实现主查询的条件不是子查询结果中时,就可以通过关键字 not in来进行判断。
查询雇员表中的数据记录,这些数据的部门编号必须在部门表中出现:
第一步: 查询所有部门的编号select deptno from employee;
第二步:select * from employee where deptno in ( select deptno from dept; )
select * from employee where deptno not in ( select deptno from dept);
-
any关键字
any用来标识主查询的条件为满足子查询返回查询结果中任意一条数据记录,any有三种匹配模式:
查询雇员中的姓名和工资,这些雇员的工资不低于职位为manager的工资
第一步:查询manager的工资:select sal from employee where job="manager";
第二步:select name, sal from employee where sal>any( select sal from employee where job="manager" );
-
all关键字
表示主查询的条件为满足子查询返回结果中的所有记录.
查询雇员的姓名和工资,这些雇员的工资高于职位为manager的工资select name, sal from employee where sal>all( select sal from employee where job="manager" );
-
exists关键字
exists是一个布尔类型,当返回结果集时为True, 不能返回结果集为false, 查询exists对外表采用遍历方式逐条查询,每次查询都会比较exists的条件语句,当exists里的条件语句返回记录行时则条件为真。此次返回当前遍历到的记录,反之,如果exists里的条件语句不能返回记录行,则丢弃当前遍历到的记录。
查询部门表中的编号、名字,如果该部门没有员工则显示该部门:
第一步:查询所有有员工的部门select * from employee join dept where employee.deptno=dept.deptno;
第二步:select * from dept where not exists( select * from employee where deptno=dept.deptno );
显示所有有员工的部门信息:select * from dept where exists( select * from employee where deptno=dept.deptno);
内表大的适合使用exists;
外表结果集合大的适合使用in;
返回多行多列子查询:
当子查询的返回结果为多行多列数据时,该子查询语句一般会在主查询语句的from子句中,被当做一张临时表的方式处理。