MySQL 的核心知识点之一

46 阅读3分钟

你提到的「链表查询」「外键连接」其实是指 多表关联查询(JOIN 查询)
MySQL 中主要有 6 种关联方式。我来给你系统讲解原理、语法、区别,并配完整例子。


🧩 一、准备示例表结构

我们以一个常见的用户-订单系统为例:

-- 用户表
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50),
  email VARCHAR(100)
);

-- 订单表
CREATE TABLE orders (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT,
  order_no VARCHAR(20),
  amount DECIMAL(10,2),
  FOREIGN KEY (user_id) REFERENCES users(id)  -- 外键关联 users.id
);

数据示例:

INSERT INTO users (name, email) VALUES
('张三', 'zhang@example.com'),
('李四', 'li@example.com'),
('王五', 'wang@example.com');

INSERT INTO orders (user_id, order_no, amount) VALUES
(1, 'A1001', 200.50),
(1, 'A1002', 150.00),
(2, 'A2001', 99.99);

🚀 二、MySQL 的 6 种 JOIN 查询方式


① INNER JOIN(内连接)

✅ 只返回两个表中匹配到的记录

SELECT users.name, orders.order_no, orders.amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;

🔍 结果:

nameorder_noamount
张三A1001200.50
张三A1002150.00
李四A200199.99

👉 没有订单的用户(王五)不会显示。


② LEFT JOIN(左连接)

✅ 返回左表的全部数据,即使右表没有匹配。

SELECT users.name, orders.order_no, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

🔍 结果:

nameorder_noamount
张三A1001200.50
张三A1002150.00
李四A200199.99
王五NULLNULL

👉 王五 没有订单,也会出现。


③ RIGHT JOIN(右连接)

✅ 与 LEFT JOIN 相反,返回右表全部数据。

SELECT users.name, orders.order_no, orders.amount
FROM users
RIGHT JOIN orders ON users.id = orders.user_id;

🔍 结果与 INNER JOIN 类似,因为订单表中的 user_id 都匹配成功。


④ FULL OUTER JOIN(全外连接)

⚠️ MySQL 不直接支持(但可以用 UNION 模拟)。
✅ 返回左表 + 右表所有数据,没有匹配的补 NULL。

SELECT users.name, orders.order_no, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id

UNION

SELECT users.name, orders.order_no, orders.amount
FROM users
RIGHT JOIN orders ON users.id = orders.user_id;

🔍 会包含所有用户和所有订单(哪怕不匹配)。


⑤ CROSS JOIN(交叉连接)

✅ 返回笛卡尔积(两表所有行两两组合)。

SELECT users.name, orders.order_no
FROM users
CROSS JOIN orders;

假设 users 3 行,orders 3 行,
👉 结果 = 3 × 3 = 9 行。


⑥ SELF JOIN(自连接)

✅ 同一张表自己关联自己,用别名区分。

例:查询每个用户的“推荐人”

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;

🧠 三、外键 (FOREIGN KEY) 的作用

外键保证数据的一致性。
比如 orders.user_id 必须在 users.id 中存在,否则不能插入。

  • ON DELETE CASCADE:删除用户时自动删除其订单。
  • ON UPDATE CASCADE:修改用户 id 时,自动更新订单表中对应的 user_id。

示例:

CREATE TABLE orders (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT,
  order_no VARCHAR(20),
  FOREIGN KEY (user_id) REFERENCES users(id)
  ON DELETE CASCADE
  ON UPDATE CASCADE
);

🔍 四、复杂多表链式 JOIN 示例

三表:用户、订单、商品:

SELECT u.name AS 用户名,
       o.order_no AS 订单号,
       p.product_name AS 商品名,
       p.price AS 单价
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN products p ON o.id = p.order_id;

这就是「链表查询」的含义:多表逐级关联


🧩 五、在 NestJS + TypeORM 中实现相同查询

TypeORM 写法:

const result = await this.userRepository
  .createQueryBuilder('u')
  .leftJoinAndSelect('u.orders', 'o')
  .leftJoinAndSelect('o.products', 'p')
  .select(['u.name', 'o.order_no', 'p.product_name', 'p.price'])
  .getMany();

对应 SQL:

SELECT u.name, o.order_no, p.product_name, p.price
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
LEFT JOIN products p ON o.id = p.order_id;

✅ 总结表格

JOIN 类型含义结果包含哪些数据
INNER JOIN内连接两边都匹配的
LEFT JOIN左连接左表全部,右表匹配的
RIGHT JOIN右连接右表全部,左表匹配的
FULL JOIN全连接所有匹配 + 不匹配的
CROSS JOIN笛卡尔积所有组合
SELF JOIN自连接一张表自己关联