前置知识: SQL

自然连接与 USING

3 min中级

NATURAL JOIN 与 USING 子句:同名列等值连接的简写语法、与 ON 的语义差异、方言支持与三大陷阱。

1. 一句话入门

ON 子句要求你逐列写出连接条件;当两张表的连接列恰好同名时,SQL 提供了两种简写:

  • NATURAL JOIN:自动用两表所有同名列做等值连接,结果中同名列只保留一份。
  • USING (col, …):只把指定的同名列用于连接,结果中这些列同样合并为一份。

两者都是 SQL:92 标准语法,但语义上是”隐式契约”——表结构一变,连接条件就跟着变,因此生产代码里要多加小心。

2. 准备练习数据

CREATE TABLE departments (
    dept_id   INT PRIMARY KEY,
    dept_name VARCHAR(100)
);

CREATE TABLE employees (
    emp_id  INT PRIMARY KEY,
    name    VARCHAR(100),
    dept_id INT
);

INSERT INTO departments VALUES (1, '研发'), (2, '销售'), (3, '人事');
INSERT INTO employees VALUES
    (101, '张三', 1),
    (102, '李四', 2),
    (103, '王五', NULL);   -- 没有部门

3. NATURAL JOIN:自动同名连接

3.1 基本语义

SELECT * FROM employees NATURAL JOIN departments;

两表唯一的同名列是 dept_id,所以等价于:

SELECT e.emp_id, e.name, e.dept_id, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

预期结果(注意 dept_id 只出现一次,王五因 dept_id 为 NULL 被内连接过滤掉):

emp_id | name | dept_id | dept_name
101    | 张三 | 1       | 研发
102    | 李四 | 2       | 销售

3.2 多个同名列:全部参与连接

若两表共有 dept_id 和 region 两列,NATURAL JOIN 等价于:

SELECT e.dept_id, e.region, e.name, d.dept_name
FROM employees e
INNER JOIN departments d
    ON e.dept_id = d.dept_id AND e.region = d.region;

这既是便利也是风险:你未必意识到”所有”同名列都成了连接条件。

3.3 三大陷阱

-- 陷阱1:加列改变语义
-- 之后有人执行:ALTER TABLE departments ADD COLUMN name VARCHAR(100);
-- NATURAL JOIN 立刻变成按 (dept_id, name) 双列连接,
-- 而"研发部"的 name 与员工姓名几乎不可能相同 → 查询悄悄返回空集

-- 陷阱2:隐式行为难追踪
SELECT * FROM a NATURAL JOIN b NATURAL JOIN c;
-- 需要逐表检查全部同名列才能确定真实连接条件

-- 陷阱3:NULL 永远不相等
-- dept_id 为 NULL 的行(如王五)在任何等值连接(含 NATURAL JOIN)中都不匹配
-- 想保留它请用 NATURAL LEFT JOIN

最佳实践:生产代码避免 NATURAL JOIN,改用显式 JOIN ... ON,让连接条件随表结构演进保持稳定。

4. USING:指定同名列的简写

4.1 基本语义

SELECT * FROM employees
JOIN departments USING (dept_id);

与 NATURAL JOIN 的区别:只使用你点名的列做连接,其余同名列不参与。

4.2 USING 与 ON 的区别

特性ON 子句USING 子句
列指定可使用不同名列只能使用同名列
条件类型任意条件(不等值也可)仅等值条件
结果列两表同名列各保留一份被指定的同名列合并一份
限定符结果列可用表别名限定合并列不可再加表别名
-- USING 合并列之后,不能再写 e.dept_id 或 d.dept_id
SELECT dept_id          -- 正确
FROM employees e
JOIN departments d USING (dept_id);

-- SELECT e.dept_id ... USING (dept_id)  -- 错误:合并列不能用别名限定

4.3 多列 USING 与链式连接

-- 多列
SELECT * FROM employees e
JOIN departments d USING (dept_id, region);

-- 多表链式连接,配合规范化的同名外键非常简洁
SELECT *
FROM orders o
JOIN order_items oi USING (order_id)
JOIN products p USING (product_id);

4.4 NATURAL / USING 的外连接变体

SELECT * FROM departments NATURAL LEFT JOIN employees;
-- 保留所有部门,无员工的部门填 NULL(含人事部,共 3 行)

SELECT * FROM employees NATURAL RIGHT JOIN departments;

SELECT * FROM employees NATURAL FULL JOIN departments;
-- 两表所有行都保留,不匹配侧填 NULL

方言注意:MySQL 不支持 FULL [OUTER] JOIN(自然连接变体同样不支持),需要用 “LEFT JOIN + UNION + RIGHT JOIN(排除重复)“模拟;SQLite 3.39.0 起才支持 RIGHT/FULL JOIN。参见第 6 节方言表。

5. 同族的连接写法

5.1 不等值连接(只能用 ON)

-- 按薪资区间匹配职级,USING/NATURAL 无法表达
SELECT e.name, e.salary, g.grade
FROM employees e
JOIN salary_grades g
  ON e.salary BETWEEN g.min_sal AND g.max_sal;

5.2 CROSS JOIN:笛卡尔积

-- 3 种颜色 x 4 种尺寸 = 12 行,无连接条件,天然不需要 ON/USING
SELECT * FROM colors CROSS JOIN sizes;

5.3 多表连接

SELECT e.name, d.dept_name, p.project_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
JOIN projects p ON e.emp_id = p.lead_id;

6. 方言差异速查

特性PostgreSQLMySQLSQLiteSQL Server
NATURAL JOIN支持支持支持不支持
USING 子句支持支持支持不支持
FULL [OUTER] JOIN支持不支持3.39.0+支持
RIGHT/FULL + NATURAL支持不支持 FULL3.39.0+不支持 NATURAL

MySQL 模拟 FULL OUTER JOIN 的标准写法:

SELECT e.name, d.dept_name
FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id
UNION
SELECT e.name, d.dept_name
FROM employees e RIGHT JOIN departments d ON e.dept_id = d.dept_id;
-- UNION 自带去重,恰好去掉两侧都匹配的重复行

7. 实践建议与小结

何时可以用 USING:外键列与主键列同名(规范命名如 dept_id)、连接条件简单、 表结构稳定。设计阶段统一外键命名能让 USING 发挥最大价值。

何时应避免 NATURAL JOIN:表可能被他人加列、多表连接、长期维护的生产代码。

小结:

  • NATURAL JOIN = 全部同名列的等值连接 + 同名列合并;USING = 指定同名列的等值连接 + 合并。
  • 合并列不能再用表别名限定;需要区分两侧值时退回 ON。
  • 同名列参与连接是”隐式契约”,ALTER TABLE ADD COLUMN 会静默改变 NATURAL JOIN 语义。
  • SQL Server 两样都不支持;MySQL 没有 FULL JOIN,用 UNION 模拟。
  • 生产代码首选显式 JOIN ... ON:意图明确、可移植、抗结构变更。