我們之前學習的SELECT語句都是單表查詢,隨着我們使用數據庫的靈活性越來越高,當我們需要的數據在不同的表中時,就需要使用多表查詢來檢索我們需要的數據。
多表查詢分爲橫向連接查詢和縱向合併查詢。
連接查詢是通過多張表中共有的關鍵字段,將多張表連成一張虛擬結果集,來補充字段信息,我們可以按照業務需求檢索多張表中的數據。
SQL查詢的基本原理:
- 單表查詢:根據where條件過濾表中的記錄,然後根據select指定的列返回查詢結果。
- 兩表連接查詢:使用on條件對兩表進行連接形成一張虛擬結果集;然後根據where條件過濾結果集中的記錄,再根據select指定的列返回查詢結果。
- 多表連接查詢:先對第一個和第二個表按照兩表連接查詢,然後用用連接後的虛擬結果集和第三個表做連接查詢,以此類推,直到所有的表都連接上爲止,最終形成一張虛擬結果集,然後根據where條件過濾虛擬結果集中的記錄,再根據select指定的列返回查詢結果。
多表連接的結果通過三個屬性決定:
- 方向性:在外連接中寫在前邊的表爲左表、寫在後邊的表爲右表。
- 主附關係:主表要出所有的數據範圍,附表與主表無匹配項時標記爲null,內連接時無主附表之分。
- 對應關係:關鍵字段中有重複值的表爲多表,沒有重複值的表爲一表。
對應關係:
- 一對一
- 一對多或多對一
- 多對多
MySQL支持的連接方式:內連接和外連接(左外連接、右外連接)。
- 內連接:按照連接條件,返回兩張表中滿足條件的記錄。
select <字段名1>[,<字段名2>...,<字段名n>] from <表名1>[ inner] join <表名2> on <連接條件>;
等值連接:連接條件是兩張表中的關鍵字段取值相等。
mysql> select ename,job,hiredate,sal,dname
-> from emp
-> inner join dept
-> on emp.deptno=dept.deptno;
+--------+-----------+------------+------+------------+
| ename | job | hiredate | sal | dname |
+--------+-----------+------------+------+------------+
| clark | manager | 1981-06-09 | 2450 | accounting |
| king | persident | 1981-11-17 | 5000 | accounting |
| miller | clerk | 1982-01-23 | 1300 | accounting |
| smith | clerk | 1980-12-17 | 800 | research |
| jones | manager | 1981-04-02 | 2975 | research |
| scott | analyst | 1987-04-19 | 3000 | research |
| adams | clerk | 1987-05-23 | 1100 | research |
| ford | analyst | 1981-12-03 | 3000 | research |
| allen | salesman | 1981-02-20 | 1600 | sales |
| ward | salesman | 1981-02-22 | 1250 | sales |
| martin | salesman | 1981-09-28 | 1250 | sales |
| blake | manager | 1981-05-01 | 2850 | sales |
| turner | salesman | 1981-09-08 | 1500 | sales |
| james | clerk | 1981-12-03 | 950 | sales |
+--------+-----------+------------+------+------------+
不等值連接:連接條件是兩張表中的關鍵字段取值滿足非等值比較運算。
mysql> select ename,job,hiredate,sal,grade
-> from emp
-> inner join salgrade
-> on sal between losal and hisal;
+--------+-----------+------------+------+-------+
| ename | job | hiredate | sal | grade |
+--------+-----------+------------+------+-------+
| smith | clerk | 1980-12-17 | 800 | 1 |
| allen | salesman | 1981-02-20 | 1600 | 3 |
| ward | salesman | 1981-02-22 | 1250 | 2 |
| jones | manager | 1981-04-02 | 2975 | 4 |
| martin | salesman | 1981-09-28 | 1250 | 2 |
| blake | manager | 1981-05-01 | 2850 | 4 |
| clark | manager | 1981-06-09 | 2450 | 4 |
| scott | analyst | 1987-04-19 | 3000 | 4 |
| king | persident | 1981-11-17 | 5000 | 5 |
| turner | salesman | 1981-09-08 | 1500 | 3 |
| adams | clerk | 1987-05-23 | 1100 | 1 |
| james | clerk | 1981-12-03 | 950 | 1 |
| ford | analyst | 1981-12-03 | 3000 | 4 |
| miller | clerk | 1982-01-23 | 1300 | 2 |
+--------+-----------+------------+------+-------+
笛卡爾積連接:兩張表中的每一條記錄進行笛卡爾積組合,然後根據where條件過濾虛擬結果集中的記錄。
select <字段名1>[,<字段名2>...,<字段名n>] from <表名1>,<表名2> where <篩選條件>;
mysql> select ename,job,hiredate,sal,dname
-> from emp,dept
-> where emp.deptno=dept.deptno;
+--------+-----------+------------+------+------------+
| ename | job | hiredate | sal | dname |
+--------+-----------+------------+------+------------+
| clark | manager | 1981-06-09 | 2450 | accounting |
| king | persident | 1981-11-17 | 5000 | accounting |
| miller | clerk | 1982-01-23 | 1300 | accounting |
| smith | clerk | 1980-12-17 | 800 | research |
| jones | manager | 1981-04-02 | 2975 | research |
| scott | analyst | 1987-04-19 | 3000 | research |
| adams | clerk | 1987-05-23 | 1100 | research |
| ford | analyst | 1981-12-03 | 3000 | research |
| allen | salesman | 1981-02-20 | 1600 | sales |
| ward | salesman | 1981-02-22 | 1250 | sales |
| martin | salesman | 1981-09-28 | 1250 | sales |
| blake | manager | 1981-05-01 | 2850 | sales |
| turner | salesman | 1981-09-08 | 1500 | sales |
| james | clerk | 1981-12-03 | 950 | sales |
+--------+-----------+------------+------+------------+
自連接:通過設置表別名,將一張表虛擬成多張表。
mysql> select t1.ename as 員工姓名,t2.ename as 領導姓名
-> from emp as t1
-> inner join emp as t2
-> on t1.mgr=t2.empid;
+----------+----------+
| 員工姓名 | 領導姓名 |
+----------+----------+
| smith | ford |
| allen | blake |
| ward | blake |
| jones | king |
| martin | blake |
| blake | king |
| clark | king |
| scott | jones |
| turner | blake |
| adams | scott |
| james | blake |
| ford | jones |
| miller | clark |
+----------+----------+
表限定符.:如果表1和表2中的字段名相同,則必須使用表限定符.指定引用的是哪個表中的字段。
- 左連接:按照連接條件,返回兩張表中滿足條件的記錄,以及左表中的所有記錄,右表匹配不到顯示爲null。
select <字段名1>[,<字段名2>...,<字段名n>] from <表名1> left join <表名2> on <連接條件>;
示例:查詢每個部門的員工數(沒有員工的部門,員工數統計爲0)
mysql> select dept.deptno,dname,count(empid)
-> from dept
-> left join emp
-> on dept.deptno=emp.deptno
-> group by dept.deptno;
+--------+------------+--------------+
| deptno | dname | count(empid) |
+--------+------------+--------------+
| 10 | accounting | 3 |
| 20 | research | 5 |
| 30 | sales | 6 |
| 40 | operations | 0 |
+--------+------------+--------------+
- 右連接:按照連接條件,返回兩張表中滿足條件的記錄,以及右表中的所有記錄,左表匹配不到顯示爲null。
select <字段名1>[,<字段名2>...,<字段名n>] from <表名1> right join <表名2> on <連接條件>;
示例:查詢每個部門的員工數(沒有員工的部門,員工數統計爲0)
mysql> select dept.deptno,dname,count(empid)
-> from emp
-> right join dept
-> on dept.deptno=emp.deptno
-> group by dept.deptno;
+--------+------------+--------------+
| deptno | dname | count(empid) |
+--------+------------+--------------+
| 10 | accounting | 3 |
| 20 | research | 5 |
| 30 | sales | 6 |
| 40 | operations | 0 |
+--------+------------+--------------+
多表連接規則:
- 確定查詢的信息在哪幾張表
- 確定表和表之間的對應關係和主附關係
- 確定表和表之間的連接條件
一表作爲主表可以保證維度的完整性,多表作爲主表可以保證度量的準確性。在沒有明確表示需要保證維度完整性的情況下,優先保證度量的準確性,所以將度量值所在的表作爲主表。度量字段通常存在於多表中,因此通常情況下可以將多表作爲主表進行外連接。