中间结果放哪里
三种承载中间结果的方式各有位置:派生子查询适合只出现一次且逻辑很短的场合;公共表表达式适合在单条语句里把逻辑拆成有名字的步骤,作用范围就到语句结束;临时表适合同一个中间结果被多处引用、需要建索引或需要跨语句复用的场合,代价是多一次写入与读取,换来的是可预期的执行行为。选哪种不看写法高级与否,看中间结果要被用几次、多大、要不要索引。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 派生表 | 写在 FROM 里的子查询,只在一处使用 |
| 公共表表达式 | 语句级作用范围,可读性好,物化与否由优化器决定 |
| 临时表 | 会话级对象,可建索引、可收集统计信息,适合复用 |
| 清理方式 | 临时表在会话结束时自动删除 |
典型使用场景
- 复杂报表拆解:中间结果被多条语句引用,用临时表避免重复计算
- 大批量数据加工的中间落地:需要建索引加速后续连接
- 一次性拆解查询逻辑:用公共表表达式把步骤命名清楚
- 调试定位:把中间结果落成临时表逐步骤核对行数
- 连接池环境下的临时表管理:确认会话复用带来的数据残留风险
临时表用法示例
临时表用法示例
-- 建临时表承载中间结果
CREATE TEMPORARY TABLE tmp_user_total (
user_id BIGINT PRIMARY KEY, -- 主键即索引,后续连接可用
total DECIMAL(12,2)
);
-- 一次算好,后续多次引用
INSERT INTO tmp_user_total
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id;
-- 复用:与用户表连接输出
SELECT u.name, t.total
FROM users u
JOIN tmp_user_total t ON t.user_id = u.id;
-- 用完显式清理,避免连接池复用会话时残留
DROP TEMPORARY TABLE IF EXISTS tmp_user_total;三种方式对照
| 方式 | 作用范围 | 适用情况 |
|---|---|---|
| 派生子查询 | 当前语句 | 只出现一次、逻辑简单 |
| 公共表表达式 | 当前语句,可多次引用 | 拆解逻辑、提升可读性 |
| 临时表 | 整个会话 | 多处引用、需要索引、需要稳定执行行为 |
| 普通表(中间表) | 跨会话 | 需要跨任务复用或人工核查 |
| 物化视图 | 跨会话且可刷新 | 固定口径的汇总结果 |
连接池会让临时表的生命周期变得微妙:连接被归还后并不一定断开,临时表可能仍然存在。命名带随机后缀或在结束前显式清理,能避免上一次请求的残留数据被下一次请求读到。
把大结果写进临时表会带来额外的写入与读取开销,是否划算要看复用次数。只用一次就落临时表,通常比直接写成一个查询更慢。