文章
关于大小表join的问题
目录
在关系型数据库(包括 SQLite)中,JOIN 的写法顺序(小表写在前还是大表写在前)在逻辑上并不会改变查询结果,但在执行计划和性能上可能会有影响,具体差异主要体现在以下几个方面:#
1. 逻辑顺序 vs. 实际执行顺序#
- 逻辑顺序:你在 SQL 语句里写的是“大表 JOIN 小表”还是“小表 JOIN 大表”,这是给人的一种阅读习惯 —— 表示“先从哪张表取行,再去另一张表里找对应行”。
- 实际执行顺序:SQLite 会根据统计信息、索引、WHERE 条件等,自动选择最优的“物理”执行顺序(也就是“内部先做哪张表的扫描、再做哪张表的查找”)。所以不管你写的是哪种顺序,SQLite 通常都会重写为最优的顺序来执行。
2. 为什么会在 SQL 写法里考虑顺序#
- 可读性
- 如果你先写小表,再写大表,往往表达“我想先从小表里筛选出少量行,然后基于这些行去大表里查找”,对阅读者来说目的更明确。
- 反之,先写大表再写小表,有时让人疑惑“要先把大量行读进来再去过滤吗?”
- 手动优化(在某些数据库里)
- 虽然 SQLite 会自动优化,但在一些数据库(尤其是没有成本优化器的轻量级场景)里,写法顺序有时会直接决定执行顺序。此时把小表放前面,能减少中间结果量。
3. 性能对比:小表先驱动 vs. 大表先驱动#
假设:
- 小表 S,只有几百行;
- 大表 L,有成千上万行;
- 两者基于索引字段
id做 INNER JOIN。
写法 A:S JOIN L#
SELECT …
FROM small_table S
JOIN large_table L
ON S.id = L.small_id
WHERE S.some_col = '条件';
- 意图:先在小表 S 上过滤出很少量的行,再对每一行在大表 L 上做索引查找(快速)。
- 中间结果:很少行,能够极大减少查找次数。
写法 B:L JOIN S#
SELECT …
FROM large_table L
JOIN small_table S
ON L.small_id = S.id
WHERE S.some_col = '条件';
- 意图:先在大表 L 上扫描所有行(可能数万行),再对每一行去小表 S 上做索引查找。
- 中间结果:L 表的行数更多,尽管每次索引查找很快,但需要做更多次,会浪费 IO/CPU。
4. SQLite 的执行计划#
你可以用 EXPLAIN QUERY PLAN 来查看 SQLite 实际的执行顺序:
EXPLAIN QUERY PLAN
SELECT …
FROM large_table L
JOIN small_table S
ON L.small_id = S.id
WHERE S.some_col = '条件';
它会告诉你 SQLite:
- 是先对哪张表做扫描;
- 是全表扫描还是索引扫描;
- 中间结果如何传递给下一步。
如果 SQLite 判断“先扫描小表、再驱动大表”更优,它会自动按这种方式执行,哪怕你写的是
L JOIN S。
5. 何时手动调整写法#
- SQLite 自动优化足够:你通常不需要在意写法顺序,只要建好 索引(尤其是在 JOIN 和 WHERE 用到的列上),SQLite 会帮你挑最优的执行顺序。
- 需要强制 JOIN 顺序时:可以使用
JOIN的提示INDEXED BY或FORCE INDEX(部分数据库支持)来手动指定索引,或者用 CTE(WITH)强制先执行一个子查询再 JOIN。 - 简单场景下:把“期望先驱动的表”写在前面,更有助于阅读和调试。
🔑 小结#
- 逻辑与物理是分离的:写哪个表在前,并不必然决定 SQLite 的实际执行顺序。
- 执行效率依赖索引和优化器:更关键的是为 JOIN 条件和过滤字段建索引,使得无论先驱动哪个表,都能高效完成。
- 可读性优先:如果想先处理小表,就写
small_table JOIN large_table,这样对阅读者最直观。 - 实战中:一般让 SQLite 自主决定执行顺序即可,除非出现性能瓶颈才去用
EXPLAIN QUERY PLAN分析、并针对性调整。