首页/数据/SQL 速查/CTE、派生表与临时表怎么选

CTE、派生表与临时表怎么选

单语句内拆解逻辑用 CTE;同一中间结果被多处引用、需要索引或跨语句复用时用临时表。

SQL 速查数据更新于 2026-10-13

中间结果放哪里

三种承载中间结果的方式各有位置:派生子查询适合只出现一次且逻辑很短的场合;公共表表达式适合在单条语句里把逻辑拆成有名字的步骤,作用范围就到语句结束;临时表适合同一个中间结果被多处引用、需要建索引或需要跨语句复用的场合,代价是多一次写入与读取,换来的是可预期的执行行为。选哪种不看写法高级与否,看中间结果要被用几次、多大、要不要索引。

基本信息

适用平台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;

三种方式对照

方式作用范围适用情况
派生子查询当前语句只出现一次、逻辑简单
公共表表达式当前语句,可多次引用拆解逻辑、提升可读性
临时表整个会话多处引用、需要索引、需要稳定执行行为
普通表(中间表)跨会话需要跨任务复用或人工核查
物化视图跨会话且可刷新固定口径的汇总结果
连接池会让临时表的生命周期变得微妙:连接被归还后并不一定断开,临时表可能仍然存在。命名带随机后缀或在结束前显式清理,能避免上一次请求的残留数据被下一次请求读到。
把大结果写进临时表会带来额外的写入与读取开销,是否划算要看复用次数。只用一次就落临时表,通常比直接写成一个查询更慢。

易错点

  • 连接池环境下临时表未清理,后续请求读到上一次的残留数据,出现难以复现的结果异常
  • 把大结果反复写入临时表却不建索引,后续连接退化成全表扫描,收益被抵消
  • 以为公共表表达式一定会被物化:它可能被内联,多次引用时重复计算
  • 同一会话里重复创建同名临时表,未加条件清理时语句直接报错
  • 会话级对象跨语句使用后忘记清理,长期占用空间并在排查时被忽略

数据来源与参考

  • MySQL 8.0 Reference Manual · CREATE TEMPORARY TABLE
  • PostgreSQL Documentation · CREATE TABLE AS
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌