一文帶你掌握MySQL中的連表查詢
一、準備工作:創(chuàng)建真實業(yè)務表并插入測試數(shù)據(jù)
我們以電商場景的用戶表(user)、訂單表(order)、 商品表(product) 為例,先創(chuàng)建表并插入測試數(shù)據(jù)(注意:order是 MySQL 關鍵字,需用反引號``包裹)。
1. 創(chuàng)建表結構
-- 1. 用戶表(存儲用戶基本信息)
CREATE TABLE `user` (
user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用戶ID(主鍵)',
user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
user_phone VARCHAR(20) COMMENT '用戶手機號',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 商品表(存儲商品信息)
CREATE TABLE `product` (
product_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品ID(主鍵)',
product_name VARCHAR(100) NOT NULL COMMENT '商品名稱',
price DECIMAL(10,2) NOT NULL COMMENT '商品價格',
stock INT DEFAULT 0 COMMENT '商品庫存'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. 訂單表(存儲用戶訂單,關聯(lián)用戶表和商品表)
CREATE TABLE `order` (
order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '訂單ID(主鍵)',
user_id INT NOT NULL COMMENT '用戶ID(關聯(lián)user表的user_id)',
product_id INT NOT NULL COMMENT '商品ID(關聯(lián)product表的product_id)',
order_amount DECIMAL(10,2) NOT NULL COMMENT '訂單金額',
order_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下單時間',
-- 外鍵約束(可選,用于強制數(shù)據(jù)完整性)
FOREIGN KEY (user_id) REFERENCES `user`(user_id),
FOREIGN KEY (product_id) REFERENCES `product`(product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 插入測試數(shù)據(jù)
-- 插入用戶數(shù)據(jù)(3個用戶:張三、李四、王五,其中王五暫無訂單)
INSERT INTO `user` (user_name, user_phone) VALUES
('張三', '13800138000'),
('李四', '13900139000'),
('王五', '13700137000');
-- 插入商品數(shù)據(jù)(3個商品:手機、電腦、耳機)
INSERT INTO `product` (product_name, price, stock) VALUES
('華為Mate60', 6999.00, 100),
('蘋果MacBook Pro', 12999.00, 50),
('索尼WH-1000XM5', 2499.00, 200);
-- 插入訂單數(shù)據(jù)(4個訂單:張三買了手機和耳機,李四買了電腦,無王五的訂單,且有一個訂單關聯(lián)的商品ID為4(不存在的商品))
INSERT INTO `order` (user_id, product_id, order_amount) VALUES
(1, 1, 6999.00), -- 張三買華為Mate60
(1, 3, 2499.00), -- 張三買索尼耳機
(2, 2, 12999.00), -- 李四買蘋果電腦
(2, 4, 0.00); -- 李四買了一個不存在的商品(product_id=4)
3. 表數(shù)據(jù)預覽
| 表名 | 數(shù)據(jù)內容 |
|---|---|
| user | user_id:1(張三)、2(李四)、3(王五) |
| product | product_id:1(華為 Mate60)、2(蘋果 MacBook Pro)、3(索尼耳機) |
| order | order_id:1(1-1-6999)、2(1-3-2499)、3(2-2-12999)、4(2-4-0) |
二、各類連表查詢詳解(附語法、結果、作用)
1. 交叉連接(CROSS JOIN):笛卡爾積連接
作用
返回兩個表的笛卡爾積(表 A 的每一行與表 B 的每一行組合),無任何條件匹配,實際業(yè)務中極少直接使用,通常需配合WHERE過濾。
語法
-- 顯式交叉連接(用戶表 × 商品表) SELECT u.user_name, p.product_name FROM `user` u CROSS JOIN `product` p;
執(zhí)行結果(共 3×3=9 行)
| user_name | product_name |
|---|---|
| 張三 | 華為 Mate60 |
| 張三 | 蘋果 MacBook Pro |
| 張三 | 索尼 WH-1000XM5 |
| 李四 | 華為 Mate60 |
| 李四 | 蘋果 MacBook Pro |
| 李四 | 索尼 WH-1000XM5 |
| 王五 | 華為 Mate60 |
| 王五 | 蘋果 MacBook Pro |
| 王五 | 索尼 WH-1000XM5 |
特點
- 結果行數(shù) = 表 A 行數(shù) × 表 B 行數(shù);
- 無業(yè)務意義,僅用于測試或特殊數(shù)據(jù)生成。
2. 內連接(INNER JOIN):匹配連接(最常用)
作用
只返回兩個 / 多個表中滿足關聯(lián)條件的行(即交集),是業(yè)務中使用頻率最高的連表查詢。
語法(查詢用戶的訂單及對應商品信息)
-- 顯式內連接(推薦,可讀性高)
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u
INNER JOIN `order` o ON u.user_id = o.user_id
INNER JOIN `product` p ON o.product_id = p.product_id;
-- 隱式內連接(省略INNER JOIN,用WHERE指定條件,功能一致)
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u, `order` o, `product` p
WHERE u.user_id = o.user_id AND o.product_id = p.product_id;
執(zhí)行結果(僅保留匹配的行,共 3 行)
| user_name | order_id | product_name | order_amount |
|---|---|---|---|
| 張三 | 1 | 華為 Mate60 | 6999.00 |
| 張三 | 2 | 索尼 WH-1000XM5 | 2499.00 |
| 李四 | 3 | 蘋果 MacBook Pro | 12999.00 |
關鍵說明
- 過濾掉了:王五(無訂單)、李四的 order_id=4(product_id=4 無匹配商品);
- 可關聯(lián)多個表(上述示例關聯(lián)了 3 個表);
- 顯式內連接更符合 SQL 標準,推薦使用。
3. 外連接(OUTER JOIN):保留單側 / 雙側未匹配行
外連接分為左外連接、右外連接、全外連接(MySQL 不直接支持全外連接,需用 UNION 實現(xiàn)),核心是保留一側 / 雙側表的所有行,無匹配時顯示NULL。
(1)左外連接(LEFT JOIN / LEFT OUTER JOIN)
作用:保留左表(JOIN左側的表)的所有行,右表僅匹配滿足條件的行;若右表無匹配,右表字段顯示NULL。
語法(查詢所有用戶的訂單,包括無訂單的用戶)
-- 左外連接:保留用戶表的所有行
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u
LEFT JOIN `order` o ON u.user_id = o.user_id
LEFT JOIN `product` p ON o.product_id = p.product_id;
執(zhí)行結果(共 5 行,包含王五和李四的無效訂單)
| user_name | order_id | product_name | order_amount |
|---|---|---|---|
| 張三 | 1 | 華為 Mate60 | 6999.00 |
| 張三 | 2 | 索尼 WH-1000XM5 | 2499.00 |
| 李四 | 3 | 蘋果 MacBook Pro | 12999.00 |
| 李四 | 4 | NULL | 0.00 |
| 王五 | NULL | NULL | NULL |
關鍵說明
- 保留了左表(
user)的所有用戶:王五(無訂單,字段為 NULL); - 保留了李四的 order_id=4(product_id=4 無匹配商品,product_name 為 NULL)。
在這段左外連接 SQL 中,第一個LEFT JOIN的左表是FROM后的user表(別名u),而后續(xù)的LEFT JOIN的左表是前一個連接的結果集(即user和order連接后的臨時表)。
分步拆解說明
我們把 SQL 拆成兩步,你會更清晰:
第一步:user LEFT JOIN order
-- 左表:user(u),右表:order(o) SELECT u.user_name, o.order_id FROM `user` u LEFT JOIN `order` o ON u.user_id = o.user_id;
- 左表:
user(FROM后的主表),保留其所有行; - 右表:
order,僅匹配滿足u.user_id = o.user_id的行,無匹配則顯示NULL。
第二步:再 LEFT JOIN product
-- 左表:第一步得到的臨時表(user+order),右表:product(p) SELECT ... FROM (user u LEFT JOIN order o ON ...) -- 這是新的左表 LEFT JOIN `product` p ON o.product_id = p.product_id;
- 左表:前一次連接的結果集(
user和order連接后的臨時表),保留其所有行; - 右表:
product,僅匹配滿足o.product_id = p.product_id的行,無匹配則顯示NULL。
核心規(guī)律(通用)
在 MySQL 的多表連接中:
LEFT JOIN的左表 =LEFT JOIN關鍵字左側的表 / 結果集;LEFT JOIN的右表 =LEFT JOIN關鍵字右側的表 / 結果集;- 多表連續(xù)
LEFT JOIN時,左表會依次繼承前一次連接的結果,這也是為什么你的 SQL 能保留user表所有行的原因(因為每一步都是左連接,最終不會過濾掉user的行)。
比如你的 SQL 中,即使后續(xù)連接product,也不會丟失user表的行(比如王五無訂單的行、李四的無效訂單行都會保留)。
(2)右外連接(RIGHT JOIN / RIGHT OUTER JOIN)
作用:保留右表(JOIN右側的表)的所有行,左表僅匹配滿足條件的行;若左表無匹配,左表字段顯示NULL。
語法(查詢所有商品的訂單,包括無訂單的商品)
-- 右外連接:保留商品表的所有行
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u
INNER JOIN `order` o ON u.user_id = o.user_id
RIGHT JOIN `product` p ON o.product_id = p.product_id;
執(zhí)行結果(共 3 行,無訂單的商品顯示 NULL)
| user_name | order_id | product_name | order_amount |
|---|---|---|---|
| 張三 | 1 | 華為 Mate60 | 6999.00 |
| 李四 | 3 | 蘋果 MacBook Pro | 12999.00 |
| 張三 | 2 | 索尼 WH-1000XM5 | 2499.00 |
擴展:若要保留商品表全量(包括無訂單的商品,需調整連接順序)
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `product` p
RIGHT JOIN `order` o ON p.product_id = o.product_id
LEFT JOIN `user` u ON o.user_id = u.user_id;
-- 結果會包含product_id=4的無效訂單,以及所有商品(若有商品無訂單則顯示NULL)
(3)全外連接(FULL JOIN / FULL OUTER JOIN)
作用:保留左表和右表的所有行,無匹配的一側字段顯示NULL(即左外連接 + 右外連接的并集)。
注意:MySQL 不直接支持 FULL JOIN,需通過LEFT JOIN UNION RIGHT JOIN實現(xiàn)
語法(查詢所有用戶和所有商品的訂單,無匹配則顯示 NULL)
-- 左外連接部分:所有用戶 + 匹配的訂單/商品
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u
LEFT JOIN `order` o ON u.user_id = o.user_id
LEFT JOIN `product` p ON o.product_id = p.product_id
UNION -- UNION去重,UNION ALL保留重復行
-- 右外連接部分:所有商品 + 匹配的訂單/用戶
SELECT
u.user_name,
o.order_id,
p.product_name,
o.order_amount
FROM `user` u
RIGHT JOIN `order` o ON u.user_id = o.user_id
RIGHT JOIN `product` p ON o.product_id = p.product_id;
執(zhí)行結果(包含所有用戶、所有商品、所有訂單,無匹配則 NULL)
| user_name | order_id | product_name | order_amount |
|---|---|---|---|
| 張三 | 1 | 華為 Mate60 | 6999.00 |
| 張三 | 2 | 索尼 WH-1000XM5 | 2499.00 |
| 李四 | 3 | 蘋果 MacBook Pro | 12999.00 |
| 李四 | 4 | NULL | 0.00 |
| 王五 | NULL | NULL | NULL |
4. 自然連接(NATURAL JOIN):自動匹配同名字段
作用
自動根據(jù)兩個表中名稱相同的字段進行連接(無需手動寫ON條件),分為自然內連接、自然左 / 右外連接。
語法(自動匹配user_id字段,查詢用戶和訂單)
-- 自然內連接(自動匹配user_id字段)
SELECT
user_name,
order_id,
order_amount
FROM `user` u
NATURAL JOIN `order` o;
執(zhí)行結果(等同于 INNER JOIN ON u.user_id = o.user_id,共 4 行)
| user_name | order_id | order_amount |
|---|---|---|
| 張三 | 1 | 6999.00 |
| 張三 | 2 | 2499.00 |
| 李四 | 3 | 12999.00 |
| 李四 | 4 | 0.00 |
特點
- 自動匹配同名字段,無需寫
ON條件; - 風險高:若表中有多個同名字段(如
create_time),會同時匹配所有同名字段,導致非預期結果; - 生產環(huán)境極少使用,推薦顯式指定
ON條件。
5. 自連接(SELF JOIN):表自身連接
作用
將表與自身進行連接(把一個表當作兩個表使用),用于查詢表內的層級關系或關聯(lián)數(shù)據(jù)(如員工與上級、分類與子分類)。
擴展:新增員工表并演示自連接
-- 創(chuàng)建員工表(包含員工ID和上級ID,上級ID關聯(lián)自身的員工ID)
CREATE TABLE `employee` (
emp_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '員工ID',
emp_name VARCHAR(50) NOT NULL COMMENT '員工姓名',
manager_id INT COMMENT '上級ID(關聯(lián)emp_id)',
department VARCHAR(50) COMMENT '部門'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入測試數(shù)據(jù)
INSERT INTO `employee` (emp_name, manager_id, department) VALUES
('馬云', NULL, '董事會'), -- 馬云無上級
('張勇', 1, 'CEO辦公室'), -- 張勇的上級是馬云
('王堅', 2, '阿里云'), -- 王堅的上級是張勇
('蔣芳', 2, '廉政部'); -- 蔣芳的上級是張勇
-- 自連接查詢:每個員工的姓名及對應的上級姓名
SELECT
e.emp_name AS 員工姓名,
m.emp_name AS 上級姓名,
e.department AS 部門
FROM `employee` e
LEFT JOIN `employee` m ON e.manager_id = m.emp_id;
執(zhí)行結果
| 員工姓名 | 上級姓名 | 部門 |
|---|---|---|
| 馬云 | NULL | 董事會 |
| 張勇 | 馬云 | CEO 辦公室 |
| 王堅 | 張勇 | 阿里云 |
| 蔣芳 | 張勇 | 廉政部 |
特點
- 本質是內連接 / 外連接的特殊形式(表自身關聯(lián));
- 必須為表取不同的別名(如
e代表員工,m代表上級); - 常用于處理層級數(shù)據(jù)(如組織架構、評論回復)。
三、各類連表查詢的核心差異對比表
| 連接類型 | 核心邏輯 | 結果集特點 | 匹配條件指定方式 | 常用場景 |
|---|---|---|---|---|
| 交叉連接(CROSS) | 笛卡爾積,無匹配 | 行數(shù) = 表 A× 表 B,無意義組合多 | 無(或 WHERE 過濾) | 測試數(shù)據(jù)、特殊數(shù)據(jù)生成 |
| 內連接(INNER) | 僅匹配滿足條件的行(交集) | 只保留匹配行,無 NULL | ON/WHERE | 業(yè)務核心查詢(如用戶 + 訂單) |
| 左外連接(LEFT) | 保留左表所有行,右表匹配 | 左表全量,右表無匹配則 NULL | ON | 查主表全量 + 關聯(lián)表數(shù)據(jù)(所有用戶 + 訂單) |
| 右外連接(RIGHT) | 保留右表所有行,左表匹配 | 右表全量,左表無匹配則 NULL | ON | 查關聯(lián)表全量 + 主表數(shù)據(jù)(所有訂單 + 用戶) |
| 全外連接(FULL) | 保留左右表所有行(并集) | 左右表全量,無匹配則 NULL | ON(MySQL 需 UNION 實現(xiàn)) | 查兩個表的所有數(shù)據(jù)(極少用) |
| 自然連接(NATURAL) | 自動匹配同名字段 | 同內 / 外連接,但依賴字段名 | 自動(無需 ON) | 簡單場景(不推薦生產使用) |
| 自連接(SELF) | 表自身關聯(lián) | 同內 / 外連接,處理表內層級 | ON(表別名區(qū)分) | 層級數(shù)據(jù)查詢(員工 - 上級、分類 - 子分類) |
四、關鍵注意事項
關聯(lián)鍵索引:連表查詢的關聯(lián)鍵(如user.user_id、order.user_id)需建立索引,否則會觸發(fā)全表掃描,性能極差;
NULL 值處理:外連接的 NULL 字段需用IFNULL()/COALESCE()處理(如IFNULL(o.order_amount, 0.00)),避免業(yè)務邏輯異常;
表別名:多表連接時建議使用表別名(如u代表user,o代表order),簡化 SQL 并提高可讀性;
優(yōu)先級:JOIN的執(zhí)行優(yōu)先級高于WHERE,因此關聯(lián)條件寫在ON中比WHERE中更高效(尤其是外連接)。
到此這篇關于一文帶你掌握MySQL中的連表查詢的文章就介紹到這了,更多相關MySQL連表查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Mysql DNS反向解析導致連接超時過程分析(skip-name-resolve)
從其它地方連接MySQL數(shù)據(jù)庫的時候,有時候很慢。慢的原因有可能是MySQL進行反向DNS解析造成的,這里簡單介紹下原理,需要的朋友可以參考下2013-03-03
MySQL ERROR 1045 (28000) 錯誤的解決辦法
這篇文章主要介紹了MySQL ERROR 1045 (28000) 錯誤的解決辦法,很實用的解決方法,感興趣的小伙伴們可以參考一下2016-06-06
SQL調優(yōu)核心戰(zhàn)法之索引失效場景與Explain深度解析
在數(shù)據(jù)庫性能治理中,SQL調優(yōu)是提升系統(tǒng)吞吐量的核心抓手,本文通過六大典型索引失效場景剖析、Explain執(zhí)行計劃深度解讀及權威優(yōu)化策略,感興趣的小伙伴可以了解下2025-12-12

