局部临时表在存储过程中创建即生效,作用域限于当前会话和执行上下文,自动销毁但需防重复创建报错、跨批失效及tempdb资源竞争。

局部临时表在存储过程中创建即生效,作用域严格限定于当前会话和当前执行上下文,不需要手动清理也能自动销毁——但必须注意重复创建报错、跨批作用域丢失、以及 tempdb 资源竞争这三类高频问题。
CREATE TABLE #name 语句必须在存储过程内直接执行
SQL Server 不允许在 EXEC 或 sp_executesql 动态语句中创建供后续语句使用的局部临时表,因为动态执行属于独立批处理(batch),#表在其作用域外不可见。
- ✅ 正确写法:
CREATE TABLE #tmp (id INT, val VARCHAR(20))直接写在存储过程主体中 - ❌ 错误写法:
EXEC('CREATE TABLE #tmp (id INT)')后续INSERT INTO #tmp会报“对象名 '#tmp' 无效” - 局部临时表的生命周期从
CREATE TABLE执行完成开始,到存储过程退出(或显式DROP TABLE)结束;即使存储过程里有多个GO(实际不允许),也会中断作用域
多次执行同一存储过程前需判断并清理 #表
如果存储过程可能被反复调用(比如被应用程序循环调用),而内部又写了 CREATE TABLE #t,第二次执行时会因对象已存在而报错:“There is already an object named '#t' in the database”。
- 必须加判断:使用
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t - 不能只查
OBJECT_ID('#t')—— 这个函数在当前会话查不到局部临时表,必须指定完整三段式名称tempdb..#t - 也可以用
SELECT * FROM tempdb.sys.tables WHERE name LIKE '#t%'辅助调试,但生产代码中不建议依赖此方式判断
插入、查询、索引操作与普通表一致,但无统计信息自动更新
局部临时表支持 INSERT、UPDATE、SELECT、JOIN、CREATE INDEX 等全部 DML 和部分 DDL 操作,行为和用户表几乎一样,唯一关键差异是统计信息不会自动更新。
- 大数据量写入后(比如 >5000 行),应手动更新统计:
UPDATE STATISTICS #tmp,否则优化器可能生成低效执行计划 - 可建非聚集索引加速后续 JOIN 或 WHERE 查询:
CREATE INDEX IX_tmp_id ON #tmp(id) - 不支持
TRUNCATE(会报错),只能用DELETE FROM #tmp或重建表 - 字段类型尽量精简,避免
VARCHAR(MAX)或大量TEXT类型,否则易触发 tempdb 页拆分和日志膨胀
返回结果集时,#表数据能正常传出,但结构不可被外部引用
存储过程末尾执行 SELECT * FROM #tmp,调用方能拿到结果集;但外部无法对这个 #表做任何操作(如再 JOIN、再过滤),因为它根本不在外部作用域里。
- 这是正常设计,不是 bug —— 局部临时表天然是“一次性中间容器”
- 若需多次复用或跨模块传递,应改用表变量(小数据量)或全局临时表(
##tmp),但后者要小心并发冲突和命名污染 - 特别注意:如果存储过程中嵌套了另一个存储过程,且子过程也用了同名
#tmp,两个表完全隔离,互不影响
真正容易被忽略的是 tempdb 的 I/O 压力:一个高频运行的存储过程每秒建删上万行的 #表,会迅速拖慢整个实例。与其反复创建,不如考虑预建带合适索引的永久辅助表,或改用 CTE + 适当提示(如 OPTION (RECOMPILE))来规避临时表开销。











