第06章 MySQL多表查詢


第06章 MySQL多表查詢


多表查詢,也稱為關聯查詢,指兩個或更多個表一起完成查詢操作。

前提條件:這些一起查詢的表之間是有關系的(一對一、一對多),它們之間一定是有關聯字段,這個關聯字段可能建立了外鍵,也可能沒有建立外鍵。比如:員工表和部門表,這兩個表依靠“部門編號”進行關聯。

1. 一個案例引發的多表連接

1.1 案例說明

1554974984600

從多個表中獲取數據:

1554975020388

#案例:查詢員工的姓名及其部門名稱
SELECT last_name, department_name
FROM employees, departments;

1554975097631

查詢結果:

+-----------+----------------------+
| last_name | department_name      |
+-----------+----------------------+
| King      | Administration       |
| King      | Marketing            |
| King      | Purchasing           |
| King      | Human Resources      |
| King      | Shipping             |
| King      | IT                   |
| King      | Public Relations     |
| King      | Sales                |
| King      | Executive            |
| King      | Finance              |
| King      | Accounting           |
| King      | Treasury             |
...
| Gietz     | IT Support           |
| Gietz     | NOC                  |
| Gietz     | IT Helpdesk          |
| Gietz     | Government Sales     |
| Gietz     | Retail Sales         |
| Gietz     | Recruiting           |
| Gietz     | Payroll              |
+-----------+----------------------+
2889 rows in set (0.01 sec)

分析錯誤情況:

SELECT COUNT(employee_id) FROM employees;
#輸出107行

SELECT COUNT(department_id)FROM departments;
#輸出27行

SELECT 107*27 FROM dual;

我們把上述多表查詢中出現的問題稱為:笛卡爾積的錯誤。

1.2 笛卡爾積(或交叉連接)的理解

笛卡爾乘積是一個數學運算。假設我有兩個集合 X 和 Y,那么 X 和 Y 的笛卡爾積就是 X 和 Y 的所有可能組合,也就是第一個對象來自於 X,第二個對象來自於 Y 的所有可能。組合的個數即為兩個集合中元素個數的乘積數。

img

SQL92中,笛卡爾積也稱為交叉連接,英文是 CROSS JOIN。在 SQL99 中也是使用 CROSS JOIN表示交叉連接。它的作用就是可以把任意表進行連接,即使這兩張表不相關。在MySQL中如下情況會出現笛卡爾積:

#查詢員工姓名和所在部門名稱
SELECT last_name,department_name FROM employees,departments;
SELECT last_name,department_name FROM employees CROSS JOIN departments;
SELECT last_name,department_name FROM employees INNER JOIN departments;
SELECT last_name,department_name FROM employees JOIN departments;

1.3 案例分析與問題解決

  • 笛卡爾積的錯誤會在下面條件下產生

    • 省略多個表的連接條件(或關聯條件)
    • 連接條件(或關聯條件)無效
    • 所有表中的所有行互相連接
  • 為了避免笛卡爾積, 可以在 WHERE 加入有效的連接條件。

  • 加入連接條件后,查詢語法:

    SELECT	table1.column, table2.column
    FROM	table1, table2
    WHERE	table1.column1 = table2.column2;  #連接條件
    
    • 在 WHERE子句中寫入連接條件。
  • 正確寫法:

    #案例:查詢員工的姓名及其部門名稱
    SELECT last_name, department_name
    FROM employees, departments
    WHERE employees.department_id = departments.department_id;
    
  • 在表中有相同列時,在列名之前加上表名前綴。

2. 多表查詢分類講解

分類1:等值連接 vs 非等值連接

等值連接

1554975496900

SELECT employees.employee_id, employees.last_name, 
       employees.department_id, departments.department_id,
       departments.location_id
FROM   employees, departments
WHERE  employees.department_id = departments.department_id;

1554975522600

1554975526339

拓展1:多個連接條件與 AND 操作符

1554975606231

拓展2:區分重復的列名

  • 多個表中有相同列時,必須在列名之前加上表名前綴。
  • 在不同表中具有相同列名的列可以用表名加以區分。
SELECT employees.last_name, departments.department_name,employees.department_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;

拓展3:表的別名

  • 使用別名可以簡化查詢。

  • 列名前使用表名前綴可以提高查詢效率。

SELECT e.employee_id, e.last_name, e.department_id,
       d.department_id, d.location_id
FROM   employees e , departments d
WHERE  e.department_id = d.department_id;

需要注意的是,如果我們使用了表的別名,在查詢字段中、過濾條件中就只能使用別名進行代替,不能使用原有的表名,否則就會報錯。

阿里開發規范

強制】對於數據庫中表記錄的查詢和變更,只要涉及多個表,都需要在列名前加表的別名(或 表名)進行限定。

說明:對多表進行查詢記錄、更新記錄、刪除記錄時,如果對操作列沒有限定表的別名(或表名),並且操作列在多個表中存在時,就會拋異常。

正例:select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;

反例:在某業務中,由於多表關聯查詢語句沒有加表的別名(或表名)的限制,正常運行兩年后,最近在 某個表中增加一個同名字段,在預發布環境做數據庫變更后,線上查詢語句出現出 1052 異常:Column 'name' in field list is ambiguous。

拓展4:連接多個表

1554978354431

總結:連接 n個表,至少需要n-1個連接條件。比如,連接三個表,至少需要兩個連接條件。

練習:查詢出公司員工的 last_name,department_name, city

非等值連接

1554978442447

SELECT e.last_name, e.salary, j.grade_level
FROM   employees e, job_grades j
WHERE  e.salary BETWEEN j.lowest_sal AND j.highest_sal;

1554978477013

1554978482652

分類2:自連接 vs 非自連接

1554978514321

  • 當table1和table2本質上是同一張表,只是用取別名的方式虛擬成兩張表以代表不同的意義。然后兩個表再進行內連接,外連接等查詢。

題目:查詢employees表,返回“Xxx works for Xxx”

SELECT CONCAT(worker.last_name ,' works for ' 
       , manager.last_name)
FROM   employees worker, employees manager
WHERE  worker.manager_id = manager.employee_id ;

1554978684947

1554978690764

練習:查詢出last_name為 ‘Chen’ 的員工的 manager 的信息。

分類3:內連接 vs 外連接

除了查詢滿足條件的記錄以外,外連接還可以查詢某一方不滿足條件的記錄。

1554978955659

  • 內連接: 合並具有同一列的兩個以上的表的行, 結果集中不包含一個表與另一個表不匹配的行

  • 外連接: 兩個表在連接過程中除了返回滿足連接條件的行以外還返回左(或右)表中不滿足條件的行 ,這種連接稱為左(或右) 外連接。沒有匹配的行時, 結果表中相應的列為空(NULL)。

  • 如果是左外連接,則連接條件中左邊的表也稱為主表,右邊的表稱為從表

    如果是右外連接,則連接條件中右邊的表也稱為主表,左邊的表稱為從表

SQL92:使用(+)創建連接

  • 在 SQL92 中采用(+)代表從表所在的位置。即左或右外連接中,(+) 表示哪個是從表。

  • Oracle 對 SQL92 支持較好,而 MySQL 則不支持 SQL92 的外連接。

    #左外連接
    SELECT last_name,department_name
    FROM employees ,departments
    WHERE employees.department_id = departments.department_id(+);
    
    #右外連接
    SELECT last_name,department_name
    FROM employees ,departments
    WHERE employees.department_id(+) = departments.department_id;
    
  • 而且在 SQL92 中,只有左外連接和右外連接,沒有滿(或全)外連接。

3. SQL99語法實現多表查詢

3.1 基本語法

  • 使用JOIN...ON子句創建連接的語法結構:

    SELECT table1.column, table2.column,table3.column
    FROM table1
        JOIN table2 ON table1 和 table2 的連接條件
            JOIN table3 ON table2 和 table3 的連接條件
    

    它的嵌套邏輯類似我們使用的 FOR 循環:

    for t1 in table1:
        for t2 in table2:
           if condition1:
               for t3 in table3:
                  if condition2:
                      output t1 + t2 + t3
    

    SQL99 采用的這種嵌套結構非常清爽、層次性更強、可讀性更強,即使再多的表進行連接也都清晰可見。如果你采用 SQL92,可讀性就會大打折扣。

  • 語法說明:

    • 可以使用 ON 子句指定額外的連接條件
    • 這個連接條件是與其它條件分開的。
    • ON 子句使語句具有更高的易讀性
    • 關鍵字 JOIN、INNER JOIN、CROSS JOIN 的含義是一樣的,都表示內連接

3.2 內連接(INNER JOIN)的實現

  • 語法:
SELECT 字段列表
FROM A表 INNER JOIN B表
ON 關聯條件
WHERE 等其他子句;

題目1:

SELECT e.employee_id, e.last_name, e.department_id, 
       d.department_id, d.location_id
FROM   employees e JOIN departments d
ON     (e.department_id = d.department_id);

1554979073996

1554979079395

題目2:

SELECT employee_id, city, department_name
FROM   employees e 
JOIN   departments d
ON     d.department_id = e.department_id 
JOIN   locations l
ON     d.location_id = l.location_id;

1554979110008

1554979115642

3.3 外連接(OUTER JOIN)的實現

3.3.1 左外連接(LEFT OUTER JOIN)

  • 語法:
#實現查詢結果是A
SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 關聯條件
WHERE 等其他子句;
  • 舉例:
SELECT e.last_name, e.department_id, d.department_name
FROM   employees e
LEFT OUTER JOIN departments d
ON   (e.department_id = d.department_id) ;

1554979200961

3.3.2 右外連接(RIGHT OUTER JOIN)

  • 語法:
#實現查詢結果是B
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 關聯條件
WHERE 等其他子句;
  • 舉例:
SELECT e.last_name, e.department_id, d.department_name
FROM   employees e
RIGHT OUTER JOIN departments d
ON    (e.department_id = d.department_id) ;

1554979243194

需要注意的是,LEFT JOIN 和 RIGHT JOIN 只存在於 SQL99 及以后的標准中,在 SQL92 中不存在,只能用 (+) 表示。

3.3.3 滿外連接(FULL OUTER JOIN)

  • 滿外連接的結果 = 左右表匹配的數據 + 左表沒有匹配到的數據 + 右表沒有匹配到的數據。
  • SQL99是支持滿外連接的。使用FULL JOIN 或 FULL OUTER JOIN來實現。
  • 需要注意的是,MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替。

4. UNION的使用

合並查詢結果
利用UNION關鍵字,可以給出多條SELECT語句,並將它們的結果組合成單個結果集。合並時,兩個表對應的列數和數據類型必須相同,並且相互對應。各個SELECT語句之間使用UNION或UNION ALL關鍵字分隔。

語法格式:

SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2

UNION操作符

1554979317187

UNION 操作符返回兩個查詢的結果集的並集,去除重復記錄。

UNION ALL操作符

1554979343634

UNION ALL操作符返回兩個查詢的結果集的並集。對於兩個結果集的重復部分,不去重。

注意:執行UNION ALL語句時所需要的資源比UNION語句少。如果明確知道合並數據后的結果數據不存在重復數據,或者不需要去除重復的數據,則盡量使用UNION ALL語句,以提高數據查詢的效率。

舉例:查詢部門編號>90或郵箱包含a的員工信息

#方式1
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
#方式2
SELECT * FROM employees  WHERE email LIKE '%a%'
UNION
SELECT * FROM employees  WHERE department_id>90;

舉例:查詢中國用戶中男性的信息以及美國用戶中年男性的用戶信息

SELECT id,cname FROM t_chinamale WHERE csex='男'
UNION ALL
SELECT id,tname FROM t_usmale WHERE tGender='male';

5. 7種SQL JOINS的實現

1554979255233

5.7.1 代碼實現

#中圖:內連接 A∩B
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`;
#左上圖:左外連接
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`;
#右上圖:右外連接
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
#左中圖:A - A∩B
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
#右中圖:B-A∩B
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL
#左下圖:滿外連接
# 左中圖 + 右上圖  A∪B
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL  #沒有去重操作,效率高
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;
#右下圖
#左中圖 + 右中圖  A ∪B- A∩B 或者 (A -  A∩B) ∪ (B - A∩B)
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL

5.7.2 語法格式小結

  • 左中圖
#實現A -  A∩B
select 字段列表
from A表 left join B表
on 關聯條件
where 從表關聯字段 is null and 等其他子句;
  • 右中圖
#實現B -  A∩B
select 字段列表
from A表 right join B表
on 關聯條件
where 從表關聯字段 is null and 等其他子句;
  • 左下圖
#實現查詢結果是A∪B
#用左外的A,union 右外的B
select 字段列表
from A表 left join B表
on 關聯條件
where 等其他子句

union 

select 字段列表
from A表 right join B表
on 關聯條件
where 等其他子句;
  • 右下圖
#實現A∪B -  A∩B  或   (A -  A∩B) ∪ (B - A∩B)
#使用左外的 (A -  A∩B)  union 右外的(B - A∩B)
select 字段列表
from A表 left join B表
on 關聯條件
where 從表關聯字段 is null and 等其他子句

union

select 字段列表
from A表 right join B表
on 關聯條件
where 從表關聯字段 is null and 等其他子句

6. SQL99語法新特性

6.1 自然連接

SQL99 在 SQL92 的基礎上提供了一些特殊語法,比如 NATURAL JOIN 用來表示自然連接。我們可以把自然連接理解為 SQL92 中的等值連接。它會幫你自動查詢兩張連接表中所有相同的字段,然后進行等值連接

在SQL92標准中:

SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
AND e.`manager_id` = d.`manager_id`;

在 SQL99 中你可以寫成:

SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;

6.2 USING連接

當我們進行連接的時候,SQL99還支持使用 USING 指定數據表里的同名字段進行等值連接。但是只能配合JOIN一起使用。比如:

SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);

你能看出與自然連接 NATURAL JOIN 不同的是,USING 指定了具體的相同的字段名稱,你需要在 USING 的括號 () 中填入要指定的同名字段。同時使用 JOIN...USING 可以簡化 JOIN ON 的等值連接。它與下面的 SQL 查詢結果是相同的:

SELECT employee_id,last_name,department_name
FROM employees e ,departments d
WHERE e.department_id = d.department_id;

7. 章節小結

表連接的約束條件可以有三種方式:WHERE, ON, USING

  • WHERE:適用於所有關聯查詢

  • ON:只能和JOIN一起使用,只能寫關聯條件。雖然關聯條件可以並到WHERE中和其他條件一起寫,但分開寫可讀性更好。

  • USING:只能和JOIN一起使用,而且要求兩個關聯字段在關聯表中名稱一致,而且只能表示關聯字段值相等

#關聯條件
#把關聯條件寫在where后面
SELECT last_name,department_name 
FROM employees,departments 
WHERE employees.department_id = departments.department_id;

#把關聯條件寫在on后面,只能和JOIN一起使用
SELECT last_name,department_name 
FROM employees INNER JOIN departments 
ON employees.department_id = departments.department_id;

SELECT last_name,department_name 
FROM employees CROSS JOIN departments 
ON employees.department_id = departments.department_id;

SELECT last_name,department_name  
FROM employees JOIN departments 
ON employees.department_id = departments.department_id;

#把關聯字段寫在using()中,只能和JOIN一起使用
#而且兩個表中的關聯字段必須名稱相同,而且只能表示=
#查詢員工姓名與基本工資
SELECT last_name,job_title
FROM employees INNER JOIN jobs USING(job_id);

#n張表關聯,需要n-1個關聯條件
#查詢員工姓名,基本工資,部門名稱
SELECT last_name,job_title,department_name FROM employees,departments,jobs 
WHERE employees.department_id = departments.department_id 
AND employees.job_id = jobs.job_id;

SELECT last_name,job_title,department_name 
FROM employees INNER JOIN departments INNER JOIN jobs 
ON employees.department_id = departments.department_id 
AND employees.job_id = jobs.job_id;

注意:

我們要控制連接表的數量。多表連接就相當於嵌套 for 循環一樣,非常消耗資源,會讓 SQL 查詢性能下降得很嚴重,因此不要連接不必要的表。在許多 DBMS 中,也都會有最大連接表的限制。

【強制】超過三個表禁止 join。需要 join 的字段,數據類型保持絕對一致;多表關聯查詢時, 保證被關聯的字段需要有索引。

說明:即使雙表 join 也要注意表索引、SQL 性能。

來源:阿里巴巴《Java開發手冊》

附錄:常用的 SQL 標准有哪些

在正式開始講連接表的種類時,我們首先需要知道 SQL 存在不同版本的標准規范,因為不同規范下的表連接操作是有區別的。

SQL 有兩個主要的標准,分別是 SQL92SQL99。92 和 99 代表了標准提出的時間,SQL92 就是 92 年提出的標准規范。當然除了 SQL92 和 SQL99 以外,還存在 SQL-86、SQL-89、SQL:2003、SQL:2008、SQL:2011 和 SQL:2016 等其他的標准。

這么多標准,到底該學習哪個呢?實際上最重要的 SQL 標准就是 SQL92 和 SQL99。一般來說 SQL92 的形式更簡單,但是寫的 SQL 語句會比較長,可讀性較差。而 SQL99 相比於 SQL92 來說,語法更加復雜,但可讀性更強。我們從這兩個標准發布的頁數也能看出,SQL92 的標准有 500 頁,而 SQL99 標准超過了 1000 頁。實際上從 SQL99 之后,很少有人能掌握所有內容,因為確實太多了。就好比我們使用 Windows、Linux 和 Office 的時候,很少有人能掌握全部內容一樣。我們只需要掌握一些核心的功能,滿足日常工作的需求即可。

SQL92 和 SQL99 是經典的 SQL 標准,也分別叫做 SQL-2 和 SQL-3 標准。也正是在這兩個標准發布之后,SQL 影響力越來越大,甚至超越了數據庫領域。現如今 SQL 已經不僅僅是數據庫領域的主流語言,還是信息領域中信息處理的主流語言。在圖形檢索、圖像檢索以及語音檢索中都能看到 SQL 語言的使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM