数据库设计讲究范式,数据往往被拆分到多张表中存储,比如员工信息放在员工表,部门信息放在部门表,订单信息放在订单表。查询时如果需要把这些分散的数据拼到一起,就要用到连接查询。其中内连接(INNER JOIN)是最常见的形式:它只返回两张表中连接条件相匹配的记录,匹配不上的行会被直接过滤掉。掌握INNER JOIN的写法和匹配规则,是写好SQL的基础功。

一、内连接的基本语法与执行逻辑
内连接的标准语法使用INNER JOIN关键字,并通过ON子句指定连接条件。以最常见的两表连接为例:
SELECT e.emp_name, e.salary, d.dept_name
FROM employee e
INNER JOIN department d
ON e.dept_id = d.dept_id;
这段SQL的含义是:从employee表和department表中,把dept_id相等的行配对取出。employee表的别名为e,department表的别名为d,使用别名后列的引用会简洁很多,也是连接查询的推荐写法。INNER关键字可以省略,直接写JOIN默认就是内连接,但为了SQL的可读性,建议显式写出INNER JOIN。
从执行逻辑上理解,内连接会遍历左表的每一行,去右表中寻找满足ON条件的行,找到了就组合成结果行,找不到则左表这一行不会出现在结果里。反之亦然,右表中没有匹配的行同样被丢弃。这就是内连接最核心的特性:只保留两表都匹配的记录。假设某个员工没有分配部门,或者某个部门暂时没有员工,这些数据都不会出现在上述查询结果中。
内连接还支持另一种老旧的写法,即在FROM中列出多个表名,把连接条件写到WHERE里:
SELECT e.emp_name, d.dept_name FROM employee e, department d WHERE e.dept_id = d.dept_id;
这种写法在功能上与INNER JOIN等价,但可读性差,连接条件和过滤条件混在一起,一旦表数量增多很容易遗漏条件导致笛卡尔积,因此不建议在新代码中使用。
二、ON条件与WHERE过滤的区别
写内连接时一个容易混淆的点是:ON里面写的条件和WHERE里面写的条件有什么区别?对内连接来说,两者在最终结果上通常等价,但语义完全不同。ON是连接条件,负责决定两表的行如何配对;WHERE是过滤条件,负责在连接完成后对结果集再做筛选。
-- 写法一:部门条件放在ON中
SELECT e.emp_name, d.dept_name
FROM employee e
INNER JOIN department d
ON e.dept_id = d.dept_id AND d.city = '北京';
-- 写法二:部门条件放在WHERE中
SELECT e.emp_name, d.dept_name
FROM employee e
INNER JOIN department d
ON e.dept_id = d.dept_id
WHERE d.city = '北京';
上面两种写法在内连接下结果相同。但如果换成外连接,差别就非常明显:ON中的条件不满足时外连接仍会保留主表行,而WHERE中的条件不满足则整行被过滤。因此养成把连接条件写在ON、过滤条件写在WHERE的习惯,能让SQL语义更清晰,也方便日后在内外连接之间切换。
此外要特别注意连接字段的数据类型。如果e.dept_id是整型而d.dept_id是字符串类型,部分数据库会做隐式转换,不仅可能产生错误匹配,还会导致连接字段上的索引失效,查询性能大幅下降。建表时应保证关联字段类型一致。
三、多表连接与非等值连接的写法
实际业务中经常需要三张甚至更多表连接。INNER JOIN可以连续使用,按顺序依次连接即可。比如查询员工姓名、部门名称和工资等级:
SELECT e.emp_name, d.dept_name, s.grade_name
FROM employee e
INNER JOIN department d ON e.dept_id = d.dept_id
INNER JOIN salary_grade s
ON e.salary BETWEEN s.min_salary AND s.max_salary;
这个例子还展示了非等值连接:前一个连接是等值判断(等于号),后一个连接用的是BETWEEN范围判断,只要ON后面的表达式能返回布尔值,任何条件都可以作为连接条件。非等值连接常用于等级表、区间表的匹配场景。
另一个典型应用是自连接,即一张表和自己连接。比如员工表中有上级编号字段manager_id,要查每个员工及其上级的姓名,就必须借助别名把同一张表当作两个逻辑表来用:
SELECT e.emp_name AS 员工, m.emp_name AS 上级
FROM employee e
INNER JOIN employee m
ON e.manager_id = m.emp_id;
自连接时不写别名会报错,因为数据库无法区分两个同名的表。注意这类查询同样是内连接,没有上级的员工(manager_id为空)不会被查出,若要包含这类员工,需要改用LEFT JOIN。
四、内连接与外连接的对比及常见坑
内连接和左连接、右连接的本质区别在于对未匹配行的处理。通过一个简单对比可以看得很清楚:
| 连接类型 | 关键字 | 未匹配行的处理 |
|---|---|---|
| 内连接 | INNER JOIN | 两表未匹配的行都丢弃 |
| 左外连接 | LEFT JOIN | 保留左表全部行,右表无匹配则填NULL |
| 右外连接 | RIGHT JOIN | 保留右表全部行,左表无匹配则填NULL |
常见的坑有以下几个。第一,漏写ON条件会产生笛卡尔积,两表各有1万行时结果就是1亿行,数据库可能直接卡死。第二,连接字段存在NULL值时要注意,NULL与任何值比较结果都是未知,NULL所在行无法匹配,如果需要把NULL关联上,可以用COALESCE把NULL转换成默认值。第三,多表连接后行数异常增多,往往是连接条件不够精确,出现了多对多的重复匹配,需要检查是否遗漏了关联条件或数据本身存在重复。
性能方面,连接字段上建索引是提升内连接效率最直接的手段。数据库优化器一般会选择小表作为驱动表,逐行到被驱动表中通过索引查找匹配行,没有索引时退化为嵌套循环的全表扫描,表数据量大时差距非常明显。写完连接查询后,不妨用EXPLAIN查看执行计划,确认连接是否走上了索引。
总结一下,INNER JOIN的关键在于写清ON条件、区分连接与过滤的语义、合理使用别名,并根据业务判断是否需要保留未匹配的行。把这些细节掌握扎实,多表查询就能写得既准确又高效。
SQL内连接INNER JOIN多表查询修改时间:2026-09-16 03:36:33