一、联合
UNION即表和表之间的数据以纵向的方式联合到一起,本篇博客下面要讲的连接都是以横向的方式联合到一起,注意区分。
那Mysql既然不支持完全连接,那完全可以通过联合(UNION)和外连接达到完全连接查询的目的。
二、内连接
1、多表查询+where
先要搞清楚select * from A, B
返回的结果到底是什么?—返回的行数是A表与B表记录的乘积,列数是两表列数之和。而且A和B的顺序无关紧要,不写过滤条件的话,默认就是将表A中的每一条记录都与表B中的每一条记录进行组合,或者说是将表B中的每一条记录都和表A中的每一条记录进行组合。
select * from A, B == select * from B, A == select * from A, B where 1=1
从多表中查询并且通过where过滤数据(加上连接条件)其实也是一种内连接了。
注意:如果两张关联表中有两列名字相同,需用表名.列名加以区分,否则会报错!
select * from emp, dept //表的顺序也不重要
where emp.emp_dept_id=dept_id //相等条件也可以互换位置
order by emp_salary desc;
当用where进行内连接时,需要将外键表的外键和主键表的主键关联起来才能完成内连接,否则只是瞎组合。
2、join on
select * from emp
join dept
on 1=1;
//运行结果和用where做内连接不写条件一样
mysql默认就是内连接即join a on == inner join a on(只抽取公共部分)。
Mysql使用join连接表的时候不写on(连接条件)竟然也不会报错。。。垃圾。。。
select * from emp
join dept
on emp_dept_id=dept_id;
这才是两张表的成功连接。
注意:where条件必须写在join on之后!内连接时候的on既可以写连接条件也可以写过滤条件。
三、外连接
外连接时候,先出现的表在左边,后出现的表在右边。
1、左外连接
select * from emp
left join dept
on emp_dept_id=dept_id;
//将emp表左外连接到dept表上
以上面的sql语句说明,相当于emp表中每一条数据都要从dept表中寻找匹配的记录,如果dept表中能匹配上就整体输出,dept表中匹配不上的就输出左边(emp),右边(dept)都为null。以左边的表为基准---右边可为空。
注意:外连接时候on只能用作连接的条件,其他过滤条件需要放到where中,这点和内连接不同。
譬如:
select * from emp
left join dept
on emp_dept_id=dept_id and emp_id=3;
外连接的时候用on过滤数据的时候,机制猜测是先选择临时表中的所有行,再根据是左连接还是右连接进行过滤。如果是左连接,on中连接条件后面的过滤条件会针对右边的表进行过滤,符合条件右边表就原样输出,不符合条件右边表则为空;如果是右连接,on中连接条件后面的过滤条件会针对左边的表进行过滤,符合条件左边表就原样输出,不符合条件左边表则为null。本质也是以左边表为基准---右边可为空。
重新用两张表举例子:
SELECT * FROM student st
LEFT JOIN school sl
ON sl.sl_id=st.school_id
2、右外连接
以右边的表为基准---左边可为空。
SELECT * FROM student st
RIGHT JOIN school sl
ON sl.sl_id=st.school_id;
四、完全连接
既已右边为基准,又以左边为基准。
Mysql不支持。转换思路,使用两次外连接加联合解决。
SELECT * FROM student st
LEFT JOIN school sl
ON sl.sl_id=st.school_id
UNION
SELECT * FROM student st
RIGHT JOIN school sl
ON sl.sl_id=st.school_id;
五、交叉连接
交叉结果的行数是两表行数的乘积,列数是两表列数之和。
select * from emp
cross join dept;
//等价于
select * from emp dept;
六、自连接
举个很简单的例子,查询所有工资比王五高的员工的姓名及工资。
select emp_name, emp_salary from emp
where emp_salary>(
select emp_salary from emp
where emp_name='王五'
);
七、例子
1、找出姓名不包含王的所有员工中工资最高的前3名的每个员工的姓名、工资、工资等级、部门名称。
select emp_name, emp_salary, sal_level, dept_name from emp
join dept
on dept_id=emp_dept_id
join salgrade
on emp_salary between sal_low and sal_high
where emp_name not like '%王%'
order by emp_salary desc
limit 0, 3;
2、求出每个员工的姓名、部门编号、薪水和薪水的等级。
select emp_name, emp_dept_id, emp_salary, sal_level from emp
join salgrade
on emp_salary between sal_low and sal_high;
3、查找每个部门的编号、该部门所有员工的平均工资以及平均工资的等级。
select T.emp_dept_id, T.avg_sal, S.sal_level from salgrade as S
join (
select emp_dept_id, avg(emp_salary) as avg_sal from emp
group by emp_dept_id
) as T
on T.avg_sal between S.sal_low and S.sal_high
order by T.emp_dept_id asc;
Mysql要求嵌套查询的时候需要要给表取别名,否则会报错,给表取别名还不能加双引号???垃圾,列最好也别加双引号。。。。其实取别名的时候as关键字可以省略,加上可以增加可读性。
4、查找每个部门的编号、部门名称、该部门所有员工的平均工资以及平均工资的等级。
select T.emp_dept_id, D.dept_name, T.avg_sal, S.sal_level from salgrade as S
join (
select emp_dept_id, avg(emp_salary) as avg_sal from emp
group by emp_dept_id
) as T
on T.avg_sal between S.sal_low and S.sal_high
join dept as D
on T.emp_dept_id=D.dept_id
order by T.emp_dept_id asc;
5、求出emp表中所有上级的信息。
//典型的自连接查询
select * from emp
where emp_id in(select emp_manager_id from emp);
要找出非上级信息就很简单了,直接not in解决。
6、求出平均薪水最高的部门的编号和部门的平均工资。
select emp_dept_id as "部门编号", avg(emp_salary) as "平均工资" from emp
group by emp_dept_id
order by avg(emp_salary) desc
limit 0, 1;
7、除掉工资最低的人后,剩下的人中工资最低的前3个人的姓名、工资、部门编号、部门名称、工资等级输出。
select T.emp_name, T. emp_salary, D.dept_id, D.dept_name,S.sal_level from dept as D
join (
select * from emp
where emp_salary>(select min(emp_salary) from emp)
) as T
on D.dept_id=T.emp_dept_id
join salgrade as S
on T.emp_salary between S.sal_low and S.sal_high
order by T.emp_salary asc
limit 0, 3;