数据库连接

数据库连接(JOIN)详解

一、连接类型总览

下图展示了主要的连接类型及其关系:

graph TD A[JOIN连接类型] --> B[内连接 INNER JOIN] A --> C[外连接 OUTER JOIN] A --> D[交叉连接 CROSS JOIN] A --> E[自连接 SELF JOIN] C --> F[左外连接 LEFT JOIN] C --> G[右外连接 RIGHT JOIN] C --> H[全外连接 FULL JOIN] F --> I[返回左表所有记录
+ 匹配的右表记录] G --> J[返回右表所有记录
+ 匹配的左表记录] H --> K[返回左右表所有记录
未匹配的显示NULL] B --> L[只返回两个表
匹配的记录] style I fill:#e1f5e1 style J fill:#e1f5e1 style K fill:#e1f5e1 style L fill:#e1f5e1

二、连接类型详解(附图示)

1. 内连接 (INNER JOIN)

定义 :只返回两个表中匹配的行

图示

css 复制代码
表A        表B
┌───┐     ┌───┐
│ 1 │     │ 1 │
│ 2 │     │ 3 │
│ 3 │     │ 4 │
└───┘     └───┘

INNER JOIN 结果:
┌───┬───┐
│ A │ B │
├───┼───┤
│ 1 │ 1 │
│ 3 │ 3 │
└───┴───┘

SQL语法

sql 复制代码
-- 标准写法
SELECT *
FROM 表A
INNER JOIN 表B ON 表A.id = 表B.a_id;

-- 简写(不推荐)
SELECT *
FROM 表A, 表B
WHERE 表A.id = 表B.a_id;

实际示例

sql 复制代码
-- 学生表和成绩表
SELECT s.student_id, s.name, g.course, g.score
FROM students s
INNER JOIN grades g ON s.student_id = g.student_id;

2. 左外连接 (LEFT JOIN / LEFT OUTER JOIN)

定义 :返回左表所有行 + 匹配的右表行。右表无匹配则显示NULL。

图示

css 复制代码
表A(主)    表B
┌───┐       ┌───┐
│ 1 │       │ 1 │
│ 2 │       │ 3 │
│ 3 │       │ 4 │
└───┘       └───┘

LEFT JOIN 结果:
┌───┬──────┐
│ A │ B    │
├───┼──────┤
│ 1 │ 1    │
│ 2 │ NULL │  ← 表A的2在表B中无匹配
│ 3 │ 3    │
└───┴──────┘

SQL语法

sql 复制代码
SELECT *
FROM 表A
LEFT JOIN 表B ON 表A.id = 表B.a_id;

实际示例

sql 复制代码
-- 所有学生(包括没成绩的)及其成绩
SELECT s.student_id, s.name, g.course, g.score
FROM students s
LEFT JOIN grades g ON s.student_id = g.student_id;

-- 只查没有成绩的学生
SELECT s.student_id, s.name
FROM students s
LEFT JOIN grades g ON s.student_id = g.student_id
WHERE g.student_id IS NULL;

3. 右外连接 (RIGHT JOIN / RIGHT OUTER JOIN)

定义 :返回右表所有行 + 匹配的左表行。左表无匹配则显示NULL。

图示

css 复制代码
表A        表B(主)
┌───┐       ┌───┐
│ 1 │       │ 1 │
│ 2 │       │ 3 │
│ 3 │       │ 4 │
└───┘       └───┘

RIGHT JOIN 结果:
┌──────┬───┐
│ A    │ B │
├──────┼───┤
│ 1    │ 1 │
│ 3    │ 3 │
│ NULL │ 4 │  ← 表B的4在表A中无匹配
└──────┴───┘

SQL语法

sql 复制代码
SELECT *
FROM 表A
RIGHT JOIN 表B ON 表A.id = 表B.a_id;

实际示例

sql 复制代码
-- 所有课程(包括没有学生选的)及其学生
SELECT c.course_id, c.course_name, s.name
FROM students s
RIGHT JOIN courses c ON s.course_id = c.course_id;

4. 全外连接 (FULL JOIN / FULL OUTER JOIN)

定义 :返回两个表的所有行。匹配的显示数据,不匹配的显示NULL。

图示

css 复制代码
表A        表B
┌───┐       ┌───┐
│ 1 │       │ 1 │
│ 2 │       │ 3 │
│ 3 │       │ 4 │
└───┘       └───┘

FULL JOIN 结果:
┌──────┬──────┐
│ A    │ B    │
├──────┼──────┤
│ 1    │ 1    │
│ 2    │ NULL │  ← 只在表A中存在
│ 3    │ 3    │
│ NULL │ 4    │  ← 只在表B中存在
└──────┴──────┘

SQL语法

sql 复制代码
-- MySQL不支持FULL JOIN,需用UNION模拟
SELECT *
FROM 表A
LEFT JOIN 表B ON 表A.id = 表B.a_id
UNION
SELECT *
FROM 表A
RIGHT JOIN 表B ON 表A.id = 表B.a_id
WHERE 表A.id IS NULL;

-- PostgreSQL、SQL Server等支持
SELECT *
FROM 表A
FULL OUTER JOIN 表B ON 表A.id = 表B.a_id;

5. 交叉连接 (CROSS JOIN)

定义 :返回两个表的笛卡尔积(所有可能的组合)。

图示

css 复制代码
表A        表B
┌───┐       ┌───┐
│ 1 │       │ A │
│ 2 │       │ B │
└───┘       └───┘

CROSS JOIN 结果:
┌───┬───┐
│ A │ B │
├───┼───┤
│ 1 │ A │
│ 1 │ B │
│ 2 │ A │
│ 2 │ B │  ← 2×2=4行
└───┴───┘

SQL语法

sql 复制代码
-- 显式写法
SELECT *
FROM 表A
CROSS JOIN 表B;

-- 隐式写法
SELECT *
FROM 表A, 表B;

实际示例

sql 复制代码
-- 生成所有日期和产品的组合
SELECT d.date, p.product_name
FROM dates d
CROSS JOIN products p;

6. 自连接 (SELF JOIN)

定义:表与自身连接,用于查询层级关系。

实际示例

sql 复制代码
-- 员工表(每个员工有经理)
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    manager_id INT
);

-- 查询员工及其经理
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

-- 结果示例:
-- employee  | manager
-- ----------|---------
-- 张三      | 李四
-- 王五      | 李四
-- 李四      | NULL

三、连接条件详解

1. ON vs USING

sql 复制代码
-- ON:指定连接条件
SELECT *
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;

-- USING:当列名相同时可简写(仅某些数据库支持)
SELECT *
FROM orders
JOIN customers USING (customer_id);

2. 多条件连接

sql 复制代码
-- 多个连接条件
SELECT *
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id 
    AND t1.date = t2.date
    AND t1.status = 'active';

3. 连接多个表

sql 复制代码
SELECT 
    s.name AS student,
    c.course_name,
    g.score,
    t.name AS teacher
FROM students s
JOIN grades g ON s.student_id = g.student_id
JOIN courses c ON g.course_id = c.course_id
LEFT JOIN teachers t ON c.teacher_id = t.teacher_id;

四、性能优化与最佳实践

1. 连接顺序优化

sql 复制代码
-- 小表在前,大表在后(某些优化器会自动优化)
SELECT *
FROM small_table s  -- 假设100行
JOIN large_table l  -- 假设1,000,000行
ON s.id = l.s_id;

2. 使用索引

sql 复制代码
-- 在连接列上创建索引
CREATE INDEX idx_student_id ON grades(student_id);
CREATE INDEX idx_course_id ON grades(course_id);

-- 查询时会使用索引加速
EXPLAIN SELECT *
FROM students s
JOIN grades g ON s.student_id = g.student_id;

3. **避免SELECT ***

sql 复制代码
-- 不好的写法
SELECT *
FROM table1
JOIN table2 ON ...

-- 好的写法:只选择需要的列
SELECT 
    t1.id,
    t1.name,
    t2.category,
    t2.price
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.t1_id;

4. 处理NULL值

sql 复制代码
-- 使用COALESCE处理NULL
SELECT 
    s.name,
    COALESCE(g.score, 0) AS score,  -- 如果NULL显示为0
    CASE 
        WHEN g.score IS NULL THEN '缺考'
        WHEN g.score >= 60 THEN '及格'
        ELSE '不及格'
    END AS status
FROM students s
LEFT JOIN grades g ON s.student_id = g.student_id;

五、不同数据库的差异

连接类型 MySQL PostgreSQL SQL Server Oracle
INNER JOIN
LEFT JOIN
RIGHT JOIN
FULL JOIN ✗(需模拟)
CROSS JOIN
NATURAL JOIN

六、实用示例集合

示例1:电商系统查询

sql 复制代码
-- 查询所有订单详情(包括未发货的)
SELECT 
    o.order_id,
    o.order_date,
    c.customer_name,
    p.product_name,
    oi.quantity,
    oi.price,
    s.status_name
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
LEFT JOIN order_items oi ON o.order_id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.product_id
LEFT JOIN shipping_status s ON o.shipping_id = s.status_id;

示例2:社交网络好友关系

sql 复制代码
-- 查询用户A的所有好友(双向关系)
SELECT 
    u.name AS user_name,
    f.name AS friend_name,
    r.relationship_type
FROM users u
JOIN friendships fs ON u.user_id = fs.user1_id
JOIN users f ON fs.user2_id = f.user_id
LEFT JOIN relationship_types r ON fs.relationship_id = r.type_id
WHERE u.user_id = 1

UNION

SELECT 
    u.name AS user_name,
    f.name AS friend_name,
    r.relationship_type
FROM users u
JOIN friendships fs ON u.user_id = fs.user2_id
JOIN users f ON fs.user1_id = f.user_id
LEFT JOIN relationship_types r ON fs.relationship_id = r.type_id
WHERE u.user_id = 1;

七、调试技巧

1. 逐步构建复杂连接

sql 复制代码
-- 第一步:先测试一个连接
SELECT COUNT(*) FROM table1 t1
JOIN table2 t2 ON t1.id = t2.t1_id;

-- 第二步:添加更多连接
SELECT COUNT(*) FROM table1 t1
JOIN table2 t2 ON t1.id = t2.t1_id
JOIN table3 t3 ON t2.id = t3.t2_id;

-- 第三步:添加WHERE条件和选择列

2. 使用EXPLAIN分析

sql 复制代码
-- 查看查询执行计划
EXPLAIN 
SELECT *
FROM students s
JOIN grades g ON s.student_id = g.student_id;

总结

  1. INNER JOIN:要交集数据时使用
  2. LEFT JOIN:要左表全部+右表匹配时使用
  3. RIGHT JOIN:要右表全部+左表匹配时使用(可转换为LEFT JOIN)
  4. FULL JOIN:要两个表所有数据时使用
  5. CROSS JOIN:要所有组合时使用(谨慎使用)
  6. 自连接:处理层级关系时使用

记忆口诀

  • LEFT JOIN:左表是老大,全部都要有
  • RIGHT JOIN:右表是老大,全部都要有
  • INNER JOIN:只认共同的朋友
  • FULL JOIN:大家都是朋友,有没有共同点都行
相关推荐
盛小夏2点0版1 小时前
💌 Python 表白神器:一行命令,满屏爱心 + 打字机情话
后端
SoulStranded1 小时前
交叉编译mDNSResponder生成动态库so
后端
user_永1 小时前
自定义 Spring Boot Starter
后端
辭七七1 小时前
在openEuler上用Docker部署RabbitMQ消息队列实战
后端
兔子零10241 小时前
nginx 配置长跑(中):HTTPS、转发、跨域与泛域名的实战套路
后端
用户0524832491761 小时前
在 openEuler 上快速体验 PyTorch 深度学习
后端
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 VB返回最长有效子串长度
数据结构·后端·算法
卡皮巴拉_1 小时前
周末实战:我用 Trae Solo 写了一个日志解析 CLI 工具
后端
星释1 小时前
Rust 练习册 106:太空年龄计算器与宏的魔法
开发语言·后端·rust