MySQL中的内外连接用于关联多个表中的数据,我来详细解释各种连接方式的用法和区别。
连接允许我们从多个相关表中检索数据,而不是维护冗余数据。
返回两个表中匹配的行
-- 基本语法
SELECT 列名
FROM 表1
INNER JOIN 表2 ON 连接条件;
-- 示例
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
dept_id INT
);
CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50)
);
INSERT INTO employees VALUES
(1, '张三', 101),
(2, '李四', 102),
(3, '王五', 103),
(4, '赵六', NULL);
INSERT INTO departments VALUES
(101, '技术部'),
(102, '销售部'),
(103, '市场部'),
(104, '人事部');
-- 内连接示例:只返回有部门的员工
SELECT
e.emp_name,
d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
/*
结果:
张三 技术部
李四 销售部
王五 市场部
(赵六不会被显示,因为dept_id为NULL)
*/
返回左表所有行 + 右表匹配的行
-- 语法
SELECT 列名
FROM 表1
LEFT [OUTER] JOIN 表2 ON 连接条件;
-- 示例:显示所有员工,包括没有部门的员工
SELECT
e.emp_name,
d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
/*
结果:
张三 技术部
李四 销售部
王五 市场部
赵六 NULL
*/
返回右表所有行 + 左表匹配的行
-- 语法
SELECT 列名
FROM 表1
RIGHT [OUTER] JOIN 表2 ON 连接条件;
-- 示例:显示所有部门,包括没有员工的部门
SELECT
e.emp_name,
d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
/*
结果:
张三 技术部
李四 销售部
王五 市场部
NULL 人事部
*/
MySQL不支持FULL OUTER JOIN,但可以通过UNION模拟:
-- 模拟全外连接
SELECT
e.emp_name,
d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
UNION
SELECT
e.emp_name,
d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
-- 员工表添加经理ID
ALTER TABLE employees ADD COLUMN manager_id INT;
UPDATE employees SET manager_id = 3 WHERE emp_id = 1;
UPDATE employees SET manager_id = 3 WHERE emp_id = 2;
UPDATE employees SET manager_id = NULL WHERE emp_id = 3;
-- 自连接查询员工及其经理
SELECT
e1.emp_name AS '员工',
e2.emp_name AS '经理'
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.emp_id;
-- 三个表连接示例
CREATE TABLE projects (
project_id INT PRIMARY KEY,
project_name VARCHAR(50),
emp_id INT,
dept_id INT
);
-- 连接三个表
SELECT
e.emp_name,
d.dept_name,
p.project_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
INNER JOIN projects p ON e.emp_id = p.emp_id;
-- 当连接字段名称相同时
SELECT
e.emp_name,
d.dept_name
FROM employees e
INNER JOIN departments d USING(dept_id);
-- 自动匹配相同名称的列
SELECT emp_name, dept_name
FROM employees
NATURAL JOIN departments;
-- 注意:可能产生意外结果,不推荐在生产中使用
-- 等值连接(最常用)
SELECT *
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id;
-- 非等值连接
SELECT
e1.emp_name,
e2.emp_name,
e1.salary - e2.salary AS salary_diff
FROM employees e1
JOIN employees e2 ON e1.salary > e2.salary;
-- 为连接字段创建索引
CREATE INDEX idx_emp_dept ON employees(dept_id);
CREATE INDEX idx_dept_id ON departments(dept_id);
-- 将过滤条件放在ON子句中(内连接时)
SELECT *
FROM large_table l
INNER JOIN small_table s ON l.id = s.id
AND l.status = 'active'; -- 过滤条件放在ON中
-- 连接小表在前有时更高效
SELECT *
FROM small_table s
INNER JOIN large_table l ON s.id = l.id;
-- 使用连接
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
-- 使用子查询
SELECT
e.emp_name,
(SELECT dept_name FROM departments d
WHERE d.dept_id = e.dept_id) AS dept_name
FROM employees e;
-- 通常连接性能更好,尤其是涉及多表时
SELECT
c.customer_name,
o.order_id,
o.order_date,
p.product_name,
oi.quantity,
oi.price
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_items oi ON o.order_id = oi.order_id
INNER JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date >= '2024-01-01';
-- 统计各部门员工数量(包括没有员工的部门)
SELECT
d.dept_name,
COUNT(e.emp_id) AS employee_count
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
GROUP BY d.dept_id, d.dept_name;
| 连接类型 | 返回结果 | MySQL支持 | 使用场景 |
|---|---|---|---|
| INNER JOIN | 两表匹配的行 | ✅ | 最常见,需要匹配的数据 |
| LEFT JOIN | 左表所有行 + 右表匹配行 | ✅ | 需要左表全部数据 |
| RIGHT JOIN | 右表所有行 + 左表匹配行 | ✅ | 需要右表全部数据 |
| FULL JOIN | 两表所有行 | ❌(可模拟) | 需要两表全部数据 |
| CROSS JOIN | 笛卡尔积 | ✅ | 需要所有组合 |
选择合适的连接类型取决于你的业务需求:是否需要保留不匹配的行,以及要保留哪个表的全部数据。