關聯式資料庫是現代軟體系統中最重要的資料儲存機制之一,而 MySQL 作為全球最廣泛使用的開源關聯式資料庫管理系統,在網頁應用程式、企業系統和各種資料處理場景中都扮演著核心角色。理解 MySQL 的基礎架構與操作方式,是每位後端開發者和資料工程師必備的技能。
本文將從 MySQL 的系統架構開始,逐步介紹資料庫的建立與管理、SQL 查詢語法的基礎應用、資料型態的選擇策略,以及最重要的資料表連結技術。透過這些核心概念的學習,讀者將能夠建立起完整的 MySQL 知識體系,為後續的進階應用奠定穩固的基礎。
資料庫管理系統概述
資料庫管理系統是一種專門用於儲存、管理和檢索資料的軟體系統。在資訊科技的發展歷程中,資料庫技術經歷了從檔案系統、階層式資料庫、網路式資料庫到關聯式資料庫的演進過程。關聯式資料庫以其嚴謹的數學理論基礎和靈活的資料組織方式,成為當今最主流的資料儲存解決方案。
關聯式資料庫的核心概念是將資料組織成一系列相互關聯的表格,每個表格包含多個欄位和記錄。表格之間透過主鍵和外鍵建立關聯,形成完整的資料結構。這種設計方式不僅能夠有效減少資料冗餘,也能夠維護資料的一致性和完整性。
MySQL 的發展始於 1995 年,由瑞典公司 MySQL AB 開發。經過多年的發展和企業併購,MySQL 目前由 Oracle 公司維護。由於其開源特性、優異的效能和廣泛的社群支援,MySQL 成為 LAMP 架構中的重要組成部分,被大量的網頁應用程式所採用。從個人部落格到大型電商平台,MySQL 都能夠提供穩定可靠的資料儲存服務。
MySQL 系統架構解析
MySQL 的系統架構可以分為三個主要層次:連線層、服務層和儲存引擎層。這種分層架構的設計使得 MySQL 具有良好的模組化特性,開發者可以根據不同的應用需求選擇適合的元件組合。
連線層負責處理客戶端的連線請求,包括連線管理、認證驗證和安全控制等功能。當應用程式需要存取資料庫時,首先會透過連線層建立與 MySQL 伺服器的連線。連線層會驗證使用者的身分和權限,確保只有授權的使用者才能存取資料。
服務層是 MySQL 的核心處理層,負責解析 SQL 語句、執行查詢優化和快取管理等功能。當 SQL 語句進入服務層後,首先會經過解析器進行語法分析,然後由優化器產生最佳的執行計畫,最後由執行器呼叫儲存引擎完成實際的資料操作。服務層還包含查詢快取功能,可以將常用查詢的結果暫存起來,避免重複執行相同的查詢。
儲存引擎層負責實際的資料儲存和檢索工作。MySQL 支援多種儲存引擎,每種引擎都有其特定的優勢和適用場景。InnoDB 是 MySQL 的預設儲存引擎,提供交易處理、外鍵約束和列級鎖定等功能,適合需要高可靠性的應用場景。MyISAM 引擎則專注於讀取效能,適合以查詢為主的應用。Memory 引擎將資料儲存在記憶體中,提供極快的存取速度,適合暫存資料或快取應用。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
title MySQL 系統架構
rectangle "客戶端應用程式" as client
rectangle "MySQL Server" as server {
rectangle "連線層" as conn_layer {
rectangle "連線管理" as conn_mgmt
rectangle "認證驗證" as auth
rectangle "安全控制" as security
}
rectangle "服務層" as service_layer {
rectangle "SQL 解析器" as parser
rectangle "查詢優化器" as optimizer
rectangle "查詢快取" as cache
rectangle "執行器" as executor
}
rectangle "儲存引擎層" as storage_layer {
rectangle "InnoDB" as innodb
rectangle "MyISAM" as myisam
rectangle "Memory" as memory
}
}
rectangle "檔案系統" as filesystem
client -down-> conn_layer
conn_layer -down-> service_layer
service_layer -down-> storage_layer
storage_layer -down-> filesystem
@enduml
MySQL 安裝與環境設定
安裝 MySQL 的方式會因作業系統的不同而有所差異,但基本的步驟和概念是相似的。在正式環境中,建議使用官方提供的安裝套件,以確保系統的穩定性和安全性。
在 Windows 環境中,可以從 MySQL 官方網站下載 MySQL Installer,這是一個整合式的安裝工具,包含 MySQL Server、MySQL Workbench 和其他相關工具。安裝過程中,需要設定 root 帳號的密碼、選擇伺服器組態類型以及設定網路連線方式。對於開發環境,通常選擇 Development Computer 組態,這會使用較少的系統資源。對於正式環境,則應該根據實際的硬體規格選擇適當的組態。
在 macOS 環境中,除了使用官方的 DMG 安裝檔之外,也可以透過 Homebrew 套件管理工具進行安裝。使用 Homebrew 安裝的優點是可以方便地進行版本管理和更新。
# 在 macOS 使用 Homebrew 安裝 MySQL
# 此命令會自動下載並安裝最新版本的 MySQL
brew install mysql
# 啟動 MySQL 服務
# 使用 brew services 可以讓 MySQL 在系統啟動時自動執行
brew services start mysql
# 執行安全性設定精靈
# 此精靈會引導您設定 root 密碼和移除測試資料庫
mysql_secure_installation
在 Linux 環境中,不同的發行版有不同的套件管理系統。以 Ubuntu 為例,可以使用 apt 套件管理工具進行安裝。
# 在 Ubuntu 使用 apt 安裝 MySQL
# 首先更新套件索引
sudo apt update
# 安裝 MySQL Server
sudo apt install mysql-server
# 執行安全性設定精靈
sudo mysql_secure_installation
# 檢查 MySQL 服務狀態
sudo systemctl status mysql
安裝完成後,需要進行基本的安全性設定。mysql_secure_installation 精靈會引導您完成以下設定:設定 root 帳號密碼、移除匿名使用者、禁止 root 遠端登入、移除測試資料庫。這些設定對於保護資料庫的安全性非常重要,即使是開發環境也建議完成這些設定。
連線與管理工具
MySQL 提供了多種連線和管理工具,從命令列介面到圖形化工具,開發者可以根據自己的需求和偏好選擇適合的工具。
MySQL 命令列客戶端是最基本的連線工具,它提供了直接與資料庫伺服器互動的能力。透過命令列客戶端,可以執行 SQL 語句、管理資料庫和使用者、以及進行各種管理操作。
# 使用命令列客戶端連線到 MySQL
# -u 參數指定使用者名稱,-p 參數表示需要輸入密碼
mysql -u root -p
# 連線到遠端伺服器
# -h 參數指定伺服器主機名稱或 IP 位址
mysql -h 192.168.1.100 -u root -p
# 連線時直接指定資料庫
mysql -u root -p database_name
MySQL Workbench 是官方提供的圖形化管理工具,它整合了資料庫設計、SQL 開發、伺服器管理和資料遷移等功能。對於初學者來說,Workbench 提供了直觀的操作介面,可以更容易地理解資料庫的結構和關係。對於進階使用者,Workbench 也提供了強大的資料模型設計和效能分析工具。
除了官方工具之外,還有許多第三方的 MySQL 管理工具,例如 DBeaver、Navicat、HeidiSQL 等。這些工具各有其特色和優勢,開發者可以根據自己的需求進行選擇。
資料庫與資料表操作
在 MySQL 中,資料庫是資料表的容器,一個 MySQL 伺服器可以包含多個資料庫,每個資料庫又可以包含多個資料表。理解資料庫和資料表的建立與管理,是使用 MySQL 的基礎。
建立資料庫是使用 MySQL 的第一步。在建立資料庫時,可以指定字元集和排序規則,這些設定會影響資料的儲存和比較方式。對於繁體中文應用,建議使用 utf8mb4 字元集和 utf8mb4_unicode_ci 排序規則,這樣可以完整支援所有 Unicode 字元,包括表情符號。
-- 建立資料庫
-- 指定字元集為 utf8mb4,支援完整的 Unicode 字元
-- 指定排序規則為 utf8mb4_unicode_ci,提供正確的中文排序
CREATE DATABASE company_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- 顯示所有資料庫
SHOW DATABASES;
-- 選擇要使用的資料庫
-- 後續的 SQL 操作都會在這個資料庫中執行
USE company_db;
-- 顯示目前選擇的資料庫
SELECT DATABASE();
-- 刪除資料庫
-- 此操作會永久刪除資料庫及其所有資料,請謹慎使用
DROP DATABASE IF EXISTS company_db;
資料表是儲存資料的基本單位,每個資料表由多個欄位組成,每個欄位都有其名稱和資料型態。在建立資料表時,需要仔細設計欄位的結構,包括選擇適當的資料型態、設定約束條件和定義索引。
-- 建立員工資料表
-- 此資料表用於儲存公司員工的基本資訊
CREATE TABLE employees (
-- 員工編號,主鍵,自動遞增
-- INT UNSIGNED 表示無符號整數,範圍為 0 到 4,294,967,295
employee_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- 員工姓名,不允許為空值
-- VARCHAR(100) 表示最多可儲存 100 個字元
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
-- 電子郵件,設定唯一約束,確保不會有重複的郵件地址
email VARCHAR(100) UNIQUE,
-- 聯絡電話
phone VARCHAR(20),
-- 到職日期
hire_date DATE NOT NULL,
-- 薪資,使用 DECIMAL 確保精確的小數運算
-- DECIMAL(10, 2) 表示最多 10 位數,其中 2 位為小數
salary DECIMAL(10, 2),
-- 部門編號,外鍵欄位
department_id INT UNSIGNED,
-- 建立時間,預設為目前時間
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 更新時間,每次更新記錄時自動更新
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 建立部門資料表
CREATE TABLE departments (
-- 部門編號,主鍵
department_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- 部門名稱
department_name VARCHAR(100) NOT NULL,
-- 部門主管的員工編號
manager_id INT UNSIGNED,
-- 部門所在位置
location VARCHAR(100)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 為員工資料表新增外鍵約束
-- 這可以確保 department_id 的值必須存在於 departments 資料表中
ALTER TABLE employees
ADD CONSTRAINT fk_department
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE;
在建立資料表之後,還可以使用 ALTER TABLE 語句來修改資料表的結構,包括新增欄位、修改欄位屬性、刪除欄位、新增索引等操作。
-- 新增欄位
-- 在 employees 資料表中新增一個 birth_date 欄位
ALTER TABLE employees
ADD COLUMN birth_date DATE AFTER last_name;
-- 修改欄位屬性
-- 將 phone 欄位的長度改為 30 個字元
ALTER TABLE employees
MODIFY COLUMN phone VARCHAR(30);
-- 變更欄位名稱
-- 將 phone 欄位改名為 contact_phone
ALTER TABLE employees
CHANGE COLUMN phone contact_phone VARCHAR(30);
-- 刪除欄位
ALTER TABLE employees
DROP COLUMN contact_phone;
-- 新增索引以提升查詢效能
-- 為 last_name 欄位建立索引
ALTER TABLE employees
ADD INDEX idx_last_name (last_name);
-- 顯示資料表結構
DESCRIBE employees;
-- 顯示建立資料表的完整 SQL 語句
SHOW CREATE TABLE employees;
SQL 查詢基礎
SQL(Structured Query Language)是操作關聯式資料庫的標準語言,它提供了資料定義、資料操作和資料控制等功能。SELECT 語句是最常用的 SQL 語句,用於從資料表中檢索資料。
基本的 SELECT 語句由 SELECT 子句和 FROM 子句組成,SELECT 子句指定要檢索的欄位,FROM 子句指定資料來源的資料表。
-- 基本的 SELECT 語句
-- 從 employees 資料表中選取指定的欄位
SELECT first_name, last_name, email
FROM employees;
-- 使用萬用字元選取所有欄位
-- 雖然方便,但在正式環境中建議明確指定欄位名稱
SELECT *
FROM employees;
-- 使用 DISTINCT 去除重複的結果
SELECT DISTINCT department_id
FROM employees;
-- 使用欄位別名讓結果更易讀
-- AS 關鍵字可以省略,但建議保留以提升可讀性
SELECT
first_name AS 名,
last_name AS 姓,
salary AS 月薪
FROM employees;
-- 在 SELECT 中使用運算式
-- 計算年薪(月薪乘以 12)
SELECT
first_name,
last_name,
salary,
salary * 12 AS annual_salary
FROM employees;
WHERE 子句用於指定查詢條件,只有符合條件的記錄才會被檢索出來。WHERE 子句支援各種比較運算子和邏輯運算子。
-- 使用 WHERE 子句篩選資料
-- 找出薪資大於 50000 的員工
SELECT first_name, last_name, salary
FROM employees
WHERE salary > 50000;
-- 使用多個條件
-- 找出在 2020 年之後到職且薪資大於 40000 的員工
SELECT first_name, last_name, hire_date, salary
FROM employees
WHERE hire_date >= '2020-01-01'
AND salary > 40000;
-- 使用 BETWEEN 運算子
-- 找出薪資在 30000 到 50000 之間的員工
SELECT first_name, last_name, salary
FROM employees
WHERE salary BETWEEN 30000 AND 50000;
-- 使用 IN 運算子
-- 找出部門編號為 1、2 或 3 的員工
SELECT first_name, last_name, department_id
FROM employees
WHERE department_id IN (1, 2, 3);
-- 使用 LIKE 運算子進行模式比對
-- % 代表任意數量的任意字元
-- _ 代表單一任意字元
SELECT first_name, last_name, email
FROM employees
WHERE email LIKE '%@gmail.com';
-- 處理 NULL 值
-- NULL 值不能使用 = 比較,必須使用 IS NULL 或 IS NOT NULL
SELECT first_name, last_name
FROM employees
WHERE department_id IS NULL;
ORDER BY 子句用於排序查詢結果,可以指定一個或多個欄位作為排序依據,並指定升冪(ASC)或降冪(DESC)排序。
-- 依單一欄位排序
-- 依薪資降冪排序,薪資最高的排在最前面
SELECT first_name, last_name, salary
FROM employees
ORDER BY salary DESC;
-- 依多個欄位排序
-- 先依部門編號升冪排序,同一部門內再依薪資降冪排序
SELECT first_name, last_name, department_id, salary
FROM employees
ORDER BY department_id ASC, salary DESC;
-- 使用 LIMIT 限制結果數量
-- 取得薪資最高的前 10 名員工
SELECT first_name, last_name, salary
FROM employees
ORDER BY salary DESC
LIMIT 10;
-- 使用 LIMIT 和 OFFSET 進行分頁
-- OFFSET 指定要跳過的記錄數量
-- 此查詢會回傳第 11 到第 20 筆記錄
SELECT first_name, last_name, salary
FROM employees
ORDER BY salary DESC
LIMIT 10 OFFSET 10;
MySQL 資料型態詳解
選擇適當的資料型態對於資料庫的效能和儲存空間有著重要的影響。MySQL 提供了豐富的資料型態,涵蓋數值、字串、日期時間和二進位等類別。在設計資料表時,應該根據資料的特性和使用方式選擇最適合的資料型態。
數值資料型態
MySQL 提供了多種整數和浮點數資料型態,以滿足不同的數值範圍和精確度需求。
整數資料型態包括 TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT,它們的差異在於可儲存的數值範圍不同。每種整數型態都可以加上 UNSIGNED 關鍵字,將其變成無符號整數,使數值範圍向正數方向延伸。
-- 整數資料型態範例
CREATE TABLE numeric_examples (
-- TINYINT:-128 到 127(有符號)或 0 到 255(無符號)
-- 適用於儲存年齡、狀態碼等小範圍數值
age TINYINT UNSIGNED,
-- SMALLINT:-32768 到 32767 或 0 到 65535
-- 適用於儲存年份、小型計數器
year_value SMALLINT,
-- MEDIUMINT:-8388608 到 8388607 或 0 到 16777215
medium_value MEDIUMINT,
-- INT:-2147483648 到 2147483647 或 0 到 4294967295
-- 最常用的整數型態,適用於大多數場景
quantity INT,
-- BIGINT:-9223372036854775808 到 9223372036854775807
-- 適用於需要儲存極大數值的場景,如流水號
big_number BIGINT UNSIGNED
);
對於需要精確計算的數值(如金額),應該使用 DECIMAL 或 NUMERIC 型態。這兩個型態在 MySQL 中是等效的,它們使用字串形式儲存數值,可以確保精確的小數運算,不會有浮點數的精度問題。
-- DECIMAL 資料型態範例
-- DECIMAL(M, D):M 是總位數,D 是小數位數
CREATE TABLE financial_data (
-- 金額欄位:最多 10 位數,其中 2 位為小數
-- 例如:99999999.99
amount DECIMAL(10, 2),
-- 稅率欄位:最多 5 位數,其中 4 位為小數
-- 例如:0.1234(12.34%)
tax_rate DECIMAL(5, 4),
-- 不建議使用 FLOAT 或 DOUBLE 儲存金額
-- 因為浮點數會有精度問題
-- bad_example FLOAT -- 不建議
);
FLOAT 和 DOUBLE 是浮點數資料型態,適用於科學計算或不需要精確小數的場景。由於浮點數的精度限制,不建議用於儲存金融資料。
字串資料型態
MySQL 提供了多種字串資料型態,包括固定長度的 CHAR、可變長度的 VARCHAR,以及用於儲存大量文字的 TEXT 系列。
CHAR 和 VARCHAR 是最常用的字串型態。CHAR 是固定長度的字串,如果實際儲存的字串長度小於定義長度,MySQL 會自動補上空格。VARCHAR 是可變長度的字串,只佔用實際需要的儲存空間加上長度資訊。
-- 字串資料型態範例
CREATE TABLE string_examples (
-- CHAR:固定長度字串
-- 適用於長度固定的資料,如國家代碼、郵遞區號
country_code CHAR(2),
postal_code CHAR(5),
-- VARCHAR:可變長度字串
-- 適用於長度不固定的資料,如姓名、地址
name VARCHAR(100),
address VARCHAR(255),
-- ENUM:列舉型態
-- 只能儲存預先定義的值之一
status ENUM('active', 'inactive', 'pending'),
-- SET:集合型態
-- 可以儲存預先定義值的任意組合
permissions SET('read', 'write', 'delete', 'admin')
);
TEXT 系列資料型態用於儲存大量文字,包括 TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。這些型態的差異在於可儲存的最大長度不同。
-- TEXT 資料型態範例
CREATE TABLE text_examples (
-- TINYTEXT:最多 255 個字元
short_description TINYTEXT,
-- TEXT:最多 65,535 個字元(約 64 KB)
content TEXT,
-- MEDIUMTEXT:最多 16,777,215 個字元(約 16 MB)
article MEDIUMTEXT,
-- LONGTEXT:最多 4,294,967,295 個字元(約 4 GB)
document LONGTEXT
);
日期時間資料型態
MySQL 提供了 DATE、TIME、DATETIME、TIMESTAMP 和 YEAR 等日期時間資料型態,用於儲存各種時間相關的資料。
-- 日期時間資料型態範例
CREATE TABLE datetime_examples (
-- DATE:日期,格式為 YYYY-MM-DD
-- 範圍:1000-01-01 到 9999-12-31
birth_date DATE,
-- TIME:時間,格式為 HH:MM:SS
-- 可以是負值,範圍:-838:59:59 到 838:59:59
duration TIME,
-- DATETIME:日期和時間,格式為 YYYY-MM-DD HH:MM:SS
-- 範圍:1000-01-01 00:00:00 到 9999-12-31 23:59:59
event_datetime DATETIME,
-- TIMESTAMP:時間戳記
-- 範圍:1970-01-01 00:00:01 到 2038-01-19 03:14:07
-- 會自動轉換為 UTC 儲存,查詢時再轉換為連線時區
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- YEAR:年份
-- 範圍:1901 到 2155
graduation_year YEAR
);
DATETIME 和 TIMESTAMP 都可以儲存日期和時間,但它們有重要的差異。DATETIME 儲存的是絕對時間,不受時區影響。TIMESTAMP 則會將時間轉換為 UTC 儲存,查詢時再根據連線的時區設定轉換回來。因此,如果應用程式需要處理多時區的情況,TIMESTAMP 會是較好的選擇。
二進位資料型態
二進位資料型態用於儲存非文字的二進位資料,如圖片、檔案或加密資料。
-- 二進位資料型態範例
CREATE TABLE binary_examples (
-- BINARY:固定長度二進位資料
hash_value BINARY(32),
-- VARBINARY:可變長度二進位資料
encrypted_data VARBINARY(255),
-- BLOB 系列:用於儲存大型二進位物件
-- TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB
thumbnail BLOB,
original_image LONGBLOB
);
需要注意的是,雖然 MySQL 可以儲存二進位檔案,但通常不建議將大型檔案直接儲存在資料庫中。較好的做法是將檔案儲存在檔案系統或物件儲存服務中,然後在資料庫中只儲存檔案的路徑或識別碼。
資料表連結技術
在關聯式資料庫中,資料通常會分散在多個資料表中,以減少資料冗餘並維護資料的一致性。當需要查詢來自多個資料表的資料時,就需要使用資料表連結技術。MySQL 提供了多種連結類型,每種類型都有其特定的用途和行為。
內部連結
內部連結(INNER JOIN)是最常用的連結類型,它會回傳兩個資料表中符合連結條件的記錄。如果某筆記錄在任一資料表中沒有對應的匹配,該記錄就不會出現在結果中。
-- 內部連結範例
-- 查詢員工資料及其所屬部門名稱
-- 只有在 employees 和 departments 都有對應記錄的員工才會被列出
SELECT
e.first_name,
e.last_name,
e.salary,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
-- 連結多個資料表
-- 假設有一個 projects 資料表記錄員工參與的專案
SELECT
e.first_name,
e.last_name,
d.department_name,
p.project_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN project_assignments pa ON e.employee_id = pa.employee_id
INNER JOIN projects p ON pa.project_id = p.project_id;
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
title 內部連結(INNER JOIN)
rectangle "employees" as emp {
rectangle "emp_id=1\ndept_id=1" as e1
rectangle "emp_id=2\ndept_id=2" as e2
rectangle "emp_id=3\ndept_id=NULL" as e3
}
rectangle "departments" as dept {
rectangle "dept_id=1\nname=IT" as d1
rectangle "dept_id=2\nname=HR" as d2
rectangle "dept_id=3\nname=Sales" as d3
}
rectangle "結果集" as result {
rectangle "emp_id=1, dept=IT" as r1
rectangle "emp_id=2, dept=HR" as r2
}
e1 --> d1
e2 --> d2
note bottom of result
只回傳兩邊都有匹配的記錄
emp_id=3 沒有對應部門
dept_id=3 沒有對應員工
end note
@enduml
外部連結
外部連結(OUTER JOIN)會回傳一個資料表中的所有記錄,以及另一個資料表中符合條件的記錄。如果沒有匹配的記錄,結果中會以 NULL 值填充。
外部連結分為左外部連結(LEFT OUTER JOIN)和右外部連結(RIGHT OUTER JOIN)。左外部連結會回傳左側資料表的所有記錄,右外部連結則會回傳右側資料表的所有記錄。
-- 左外部連結範例
-- 列出所有員工及其部門名稱
-- 即使員工沒有指定部門(department_id 為 NULL),也會被列出
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
-- 右外部連結範例
-- 列出所有部門及其員工
-- 即使部門沒有任何員工,也會被列出
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
-- 找出沒有指定部門的員工
SELECT
e.first_name,
e.last_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
-- 找出沒有任何員工的部門
SELECT
d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE e.employee_id IS NULL;
MySQL 不直接支援 FULL OUTER JOIN(完整外部連結),但可以透過 UNION 來達成相同的效果。
-- 模擬 FULL OUTER JOIN
-- 列出所有員工和所有部門的組合
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
UNION
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
自然連結
自然連結(NATURAL JOIN)會自動根據兩個資料表中具有相同名稱的欄位進行連結。這種連結方式可以簡化 SQL 語法,但也可能因為欄位名稱的巧合而產生非預期的結果,因此在實務中使用時需要特別謹慎。
-- 自然連結範例
-- MySQL 會自動找出兩個資料表中名稱相同的欄位進行連結
-- 在此例中是 department_id
SELECT
first_name,
last_name,
department_name
FROM employees
NATURAL JOIN departments;
-- 上述查詢等同於
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
由於自然連結的隱式行為可能導致維護困難,建議在正式專案中使用明確的 JOIN 語法,清楚指定連結條件。
交叉連結
交叉連結(CROSS JOIN)會產生兩個資料表的笛卡爾積,即將第一個資料表的每一筆記錄與第二個資料表的每一筆記錄組合。結果的記錄數量等於兩個資料表記錄數量的乘積。
-- 交叉連結範例
-- 假設有一個顏色表和尺寸表,需要產生所有可能的組合
CREATE TABLE colors (
color_id INT PRIMARY KEY,
color_name VARCHAR(50)
);
CREATE TABLE sizes (
size_id INT PRIMARY KEY,
size_name VARCHAR(50)
);
-- 產生所有顏色和尺寸的組合
SELECT
c.color_name,
s.size_name
FROM colors c
CROSS JOIN sizes s;
-- 交叉連結也可以寫成
SELECT
c.color_name,
s.size_name
FROM colors c, sizes s;
交叉連結在大多數情況下會產生大量的記錄,因此需要謹慎使用。它通常用於產生測試資料或特定的組合需求。
自我連結
自我連結(Self Join)是將一個資料表與自身進行連結,通常用於處理階層式或遞迴式的資料結構,例如員工和主管的關係、分類和子分類的關係等。
-- 自我連結範例
-- 查詢員工及其直屬主管的資訊
-- 假設 employees 資料表有一個 manager_id 欄位指向主管的 employee_id
SELECT
e.first_name AS employee_first_name,
e.last_name AS employee_last_name,
m.first_name AS manager_first_name,
m.last_name AS manager_last_name
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.employee_id;
-- 找出薪資高於其主管的員工
SELECT
e.first_name,
e.last_name,
e.salary AS employee_salary,
m.first_name AS manager_name,
m.salary AS manager_salary
FROM employees e
INNER JOIN employees m ON e.manager_id = m.employee_id
WHERE e.salary > m.salary;
連結語法的變化形式
除了標準的 JOIN 語法之外,MySQL 也支援舊式的連結語法,使用逗號分隔資料表名稱,並在 WHERE 子句中指定連結條件。雖然這種語法仍然有效,但建議使用標準的 JOIN 語法,因為它更清晰、更易於維護。
-- 舊式連結語法(不建議使用)
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
-- 標準 JOIN 語法(建議使用)
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
標準 JOIN 語法的優點包括:連結條件和篩選條件分離,更容易理解查詢邏輯;可以明確指定連結類型(INNER、LEFT、RIGHT);在複雜查詢中更容易維護和除錯。
資料表別名與欄位別名
在連結查詢中,使用別名可以讓 SQL 語句更簡潔易讀。資料表別名用於縮短資料表名稱,欄位別名則用於讓結果欄位名稱更具描述性。
-- 使用資料表別名
-- e 是 employees 的別名,d 是 departments 的別名
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
-- 使用欄位別名
SELECT
e.first_name AS 員工名,
e.last_name AS 員工姓,
d.department_name AS 部門名稱,
e.salary AS 月薪,
e.salary * 12 AS 年薪
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
-- 在自我連結中使用別名區分同一資料表的不同角色
SELECT
emp.first_name AS 員工,
mgr.first_name AS 主管
FROM employees emp
LEFT JOIN employees mgr ON emp.manager_id = mgr.employee_id;
進階連結技巧
在實務應用中,經常需要處理更複雜的連結場景,包括使用子查詢作為連結來源、結合聚合函式進行分組統計,以及處理多對多關係等。
子查詢與連結
子查詢可以作為 FROM 子句中的資料來源,形成衍生資料表(Derived Table)。這種技巧在需要對資料進行預處理或聚合後再進行連結時非常有用。
-- 使用子查詢計算每個部門的平均薪資
-- 然後與員工資料連結,找出薪資高於部門平均的員工
SELECT
e.first_name,
e.last_name,
e.salary,
dept_avg.avg_salary,
d.department_name
FROM employees e
INNER JOIN (
-- 子查詢:計算每個部門的平均薪資
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
) dept_avg ON e.department_id = dept_avg.department_id
INNER JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > dept_avg.avg_salary;
連結與聚合函式
連結查詢經常需要配合聚合函式使用,以計算各種統計數據。
-- 計算每個部門的員工人數和平均薪資
SELECT
d.department_name,
COUNT(e.employee_id) AS employee_count,
AVG(e.salary) AS avg_salary,
MAX(e.salary) AS max_salary,
MIN(e.salary) AS min_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name
ORDER BY employee_count DESC;
-- 找出員工人數超過 5 人的部門
SELECT
d.department_name,
COUNT(e.employee_id) AS employee_count
FROM departments d
INNER JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name
HAVING COUNT(e.employee_id) > 5
ORDER BY employee_count DESC;
處理多對多關係
多對多關係需要透過中間資料表(也稱為連結資料表或關聯資料表)來實現。例如,員工和專案之間是多對多的關係:一個員工可以參與多個專案,一個專案也可以有多個員工參與。
-- 建立專案資料表
CREATE TABLE projects (
project_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
project_name VARCHAR(100) NOT NULL,
start_date DATE,
end_date DATE
);
-- 建立員工與專案的關聯資料表
CREATE TABLE project_assignments (
employee_id INT UNSIGNED,
project_id INT UNSIGNED,
role VARCHAR(50),
assigned_date DATE,
PRIMARY KEY (employee_id, project_id),
FOREIGN KEY (employee_id) REFERENCES employees(employee_id),
FOREIGN KEY (project_id) REFERENCES projects(project_id)
);
-- 查詢每個員工參與的專案
SELECT
e.first_name,
e.last_name,
GROUP_CONCAT(p.project_name SEPARATOR ', ') AS projects
FROM employees e
LEFT JOIN project_assignments pa ON e.employee_id = pa.employee_id
LEFT JOIN projects p ON pa.project_id = p.project_id
GROUP BY e.employee_id, e.first_name, e.last_name;
-- 查詢每個專案的參與成員
SELECT
p.project_name,
COUNT(pa.employee_id) AS member_count,
GROUP_CONCAT(
CONCAT(e.first_name, ' ', e.last_name, ' (', pa.role, ')')
SEPARATOR ', '
) AS team_members
FROM projects p
LEFT JOIN project_assignments pa ON p.project_id = pa.project_id
LEFT JOIN employees e ON pa.employee_id = e.employee_id
GROUP BY p.project_id, p.project_name;
查詢效能最佳化
在處理大量資料時,連結查詢的效能可能成為瓶頸。以下是一些提升連結查詢效能的技巧。
索引是提升查詢效能最重要的工具。對於連結查詢,應該確保連結欄位(通常是主鍵和外鍵)都有建立索引。
-- 為外鍵欄位建立索引
CREATE INDEX idx_employees_department ON employees(department_id);
CREATE INDEX idx_project_assignments_employee ON project_assignments(employee_id);
CREATE INDEX idx_project_assignments_project ON project_assignments(project_id);
-- 使用 EXPLAIN 分析查詢執行計畫
EXPLAIN SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 50000;
避免在連結條件中使用函式,因為這會導致 MySQL 無法使用索引。如果需要對欄位進行運算後再連結,可以考慮使用衍生資料表或預先計算欄位值。
-- 不好的做法:在連結條件中使用函式
SELECT *
FROM table_a a
INNER JOIN table_b b ON YEAR(a.date_column) = b.year_column;
-- 較好的做法:在資料表中預存年份欄位,或使用衍生資料表
SELECT *
FROM (
SELECT *, YEAR(date_column) AS year_value
FROM table_a
) a
INNER JOIN table_b b ON a.year_value = b.year_column;
結語
MySQL 作為最廣泛使用的開源關聯式資料庫,提供了完整的資料管理功能和豐富的 SQL 語法支援。從本文的介紹中,我們涵蓋了 MySQL 的系統架構、安裝設定、資料庫與資料表的操作、各種資料型態的選擇策略,以及資料表連結技術的實務應用。
資料表連結是關聯式資料庫最重要的特性之一,它讓我們能夠將分散在多個資料表中的資料整合起來,滿足各種複雜的查詢需求。理解不同連結類型的行為特性,並根據實際需求選擇適當的連結方式,是撰寫高效能 SQL 查詢的關鍵。
掌握這些基礎知識後,開發者可以繼續探索更進階的主題,如預存程序、觸發器、交易處理、查詢優化等。同時,隨著資料量的增長和應用場景的複雜化,也需要關注資料庫的效能調校、備份還原、高可用性架構等維運議題。MySQL 的生態系統提供了豐富的工具和資源,可以幫助開發者建立穩健可靠的資料儲存解決方案。