一、聯合
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;