SQL 连接
SQL 连接按条件组合两个关系中的行。等值连接为每个左行配上所有比较结果为 TRUE 的右行,连接类型再决定未匹配行是否保留。重复键可能使结果成倍增加,没有匹配的行则被丢弃或补 NULL。
连接按行配对,不按不同键值配对。同一个键左侧三行、右侧三行,就有九种组合;这通常不是数据库重复输出,而是条件允许这些组合。
SQL 连接如何工作
例如按部门号连接员工和办公室:
SELECT e.employee, e.dept_id, o.dept_id, o.city
FROM employees AS e
LEFT JOIN offices AS o
ON e.dept_id = o.dept_id;
对非 NULL 键 k,左组为 L_k,右组为 R_k,匹配行数为 |L_k| × |R_k|。这里遵循多重集语义,同键的两行仍是两行,不自动去重。
- INNER JOIN 只保留匹配组合。
- LEFT JOIN 还保留未匹配左行,右列补 NULL。
- FULL OUTER JOIN 进一步保留未匹配右行,左列补 NULL。
这是逻辑结果规则。数据库可用嵌套循环、哈希、归并、索引查找等物理计划产生同样的行,SQL 不承诺必须逐格构造配对矩阵。
SQL 连接逐步演示
默认 employees 有 Ada/10、Bo/20、Cy/30、Dee/40。offices 有 10 下的 NYC、London,30 下的 Paris,50 下的 Tokyo。查询使用 LEFT JOIN。
- Ada 的 10 匹配两个办公室,
1 × 2 = 2,输出(Ada, 10, 10, NYC)、(Ada, 10, 10, London),不会强制只选一个。 - Bo 的 20 无匹配,保留
(Bo, 20, NULL, NULL)。 - Cy 的 30 有一个匹配,输出
(Cy, 30, 30, Paris)。 - Dee 的 40 无匹配,输出
(Dee, 40, NULL, NULL)。 - Tokyo 只有右侧键 50,LEFT JOIN 不保护它,因此不输出。
最终五行:三个匹配组合,加两个左侧缺口。画面按源表的讲解顺序展示;没有 ORDER BY 的 SQL 不保证这个返回顺序。
结果行数为何增加
默认 INNER JOIN 有三行,LEFT JOIN 加 Bo、Dee 后五行,FULL OUTER JOIN 再加 Tokyo 后六行。多对多预设中,三个员工和三个办公室共用键 10,单组就贡献 3 × 3 = 9 行,可能超过任一输入大小。
排查时先数两侧每个键的行数,不要先加 DISTINCT。左 12、右 8 就是 96 个组合,去重可能掩盖缺失约束或 ON 条件不完整。
- 需要所有合格左右组合,用连接。
- 只问是否存在匹配,用 EXISTS。
- 每个左行只应对应一个右行,就明确是哪一个:唯一约束、更严格条件、排名或聚合,SQL 不会猜。
- 左侧缺口要保留用 LEFT,只保留匹配用 INNER,两侧缺口都需要才用 FULL OUTER。
边界情况
- 无重叠键:INNER 零行,LEFT 保留左侧,FULL 保留两侧并补 NULL。
- 普通
=下,NULL = NULL为 unknown 而非 TRUE,两空键不匹配。 - 同键左两行右四行,贡献八行,各自独立参加。
- 同键多行都未匹配,外连接逐行保留,不压成一个占位。
- 数据库方言支持不同,使用 FULL OUTER JOIN 或等价改写前需确认目标语法。
常见错误
- 预期每键一行,实际单位是满足条件的行对。
- 先 DISTINCT,再查乘法来源,抹掉数据约束和条件缺失的证据。
- LEFT JOIN 后写
WHERE o.city = 'Paris',补 NULL 行会被该条件排除,效果可能变成内连接。若条件用于决定匹配,应放在 ON;若用于筛选最终结果,才放 WHERE。 - 期待 NULL 用等号匹配 NULL,意图如此时应使用目标数据库的空值安全比较。
- 把显示顺序当连接语义,排序由 ORDER BY 指定。
- 把逻辑配对矩阵当性能轨迹,优化器可以不物化它。
模型范围
本课展示等值连接、多重集、未匹配行保留和 NULL 补列。省略投影重写、条件下推、索引、基数估计、计划选择、事务可见性、排序规则、物理连接算法。这些决定如何找行,当前课程先固定三类连接的逻辑结果。