嵌套存储过程中临时表使用的注意事项

临时表

可以创建本地临时表和全局临时表。 本地临时表仅在当前会话中可见,而全局临时表在所有会话中都可见。 临时表不能分区。

本地临时表的名称前面有一个数字符号 (#table_name),而全局临时表的名称前面有两个数字符号 (##table_name)

SQL 语句使用 CREATE TABLE 语句中为 table_name 指定的值引用临时表,例如:

复制

CREATE TABLE #MyTempTable (cola INT PRIMARY KEY);

INSERT INTO #MyTempTable VALUES (1);

如果在单个存储过程或批处理中创建了多个临时表,则它们必须有不同的名称。

如果本地临时表由存储过程创建或由多个用户同时执行的应用程序创建,则数据库引擎必须能够区分由不同用户创建的表。 为此,数据库引擎在内部为每个本地临时表的表名追加一个数字后缀。 存储在 tempdb  sysobjects 表中的临时表,其全名由 CREATE TABLE 语句中指定的表名和系统生成的数字后缀组成。 为了允许追加后缀,为本地临时表指定的 table_name 不能超过 116 个字符。

除非使用 DROP TABLE 显式删除临时表,否则临时表将在退出其作用域时由系统自动删除:

  • 当存储过程完成时,将自动删除在存储过程中创建的本地临时表。 由创建表的存储过程执行的所有嵌套存储过程都可以引用此表。 但调用创建此表的存储过程的进程无法引用此表。
  • 所有其他本地临时表在当前会话结束时都将被自动删除。
  • 全局临时表在创建此表的会话结束且其他所有任务停止对其引用时将被自动删除。 任务与表之间的关联只在单个 Transact-SQL 语句的生存周期内保持。 换言之,当创建全局临时表的会话结束时,最后一条引用此表的 Transact-SQL 语句完成后,将自动删除此表。

在存储过程或触发器中创建的本地临时表的名称可以与在调用存储过程或触发器之前创建的临时表名称相同。 但是,如果查询引用临时表,而同时有两个同名的临时表,则不定义针对哪个表解析该查询。 嵌套存储过程同样可以创建与调用它的存储过程所创建的临时表同名的临时表。 但是,为了对其进行修改以解析为在嵌套过程中创建的表,此表必须与调用过程创建的表具有相同的结构和列名。 下面的示例说明了这一点。

复制

CREATE PROCEDURE dbo.Test2
AS
    CREATE TABLE #t(x INT PRIMARY KEY);
    INSERT INTO #t VALUES (2);
    SELECT Test2Col = x FROM #t;
GO

CREATE PROCEDURE dbo.Test1
AS
    CREATE TABLE #t(x INT PRIMARY KEY);
    INSERT INTO #t VALUES (1);
    SELECT Test1Col = x FROM #t;
EXEC Test2;
GO

CREATE TABLE #t(x INT PRIMARY KEY);
INSERT INTO #t VALUES (99);
GO

EXEC Test1;
GO

下面是结果集:

(1 row(s) affected)

Test1Col

-----------

1

(1 row(s) affected)

Test2Col

-----------

2

当创建本地或全局临时表时,CREATE TABLE 语法支持除 FOREIGN KEY 约束以外的其他所有约束定义。 如果临时表中指定了 FOREIGN KEY 约束,则该语句将返回一条表明已跳过此约束的警告消息。 此表仍将创建,但不使用 FOREIGN KEY 约束。 在 FOREIGN KEY 约束中不能引用临时表。

如果某个临时表是使用命名约束创建的,并且该临时表是在用户定义的事务的作用域内创建的,则一次只能有一个用户执行创建该临时表的语句。 例如,如果某一存储过程使用命名主键约束创建一个临时表,则多个用户无法同时执行该存储过程。

来自 <CREATE TABLE (Transact-SQL) | Microsoft Learn>

引申:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

IF EXISTS(SELECT 1 FROM sys.objects WHERE object_id= OBJECT_ID(N'dbo.PRC_SUB_TEST' ) AND OBJECTPROPERTY(object_id, 'IsProcedure')=1)

BEGIN

  DROP PROCEDURE dbo.PRC_SUB_TEST;

END

GO

CREATE PROCEDURE dbo.PRC_SUB_TEST

AS

BEGIN

   

  SELECT * FROM #tmp_test;

  CREATE TABLE #tmp_test(name VARCHAR(128));

  

  INSERT INTO #tmp_test

  SELECT name FROM sys.objects

  

  SELECT * FROM #tmp_test;

END

GO

通过实验验证,我们发现外层存储过程的临时表在内层存储过程中有效,它的作用域是在内层存储过程的同名临时表创建之前,这个跟高级语言中的全局变量和局部变量作用域有点类似

 

<think>好的,用户想了解在存储过程中如何定义和使用临时表。我需要先回顾一下不同数据库系统的实现,比如SQLServer、Oracle以及可能提到的其他系统如MySQL。根据提供的引用资料,用户可能已经接触过SQLServer和Oracle的例子,所以我需要涵盖这两个主要系统。首先,临时表的作用域和生命周期是关键点。在SQL Server中,临时表分为本地(#开头)和全局(##开头),本地临时表仅在当前会话可见,存储过程结束后自动删除。而Oracle的临时表定义方式不同,数据是会话或事务级别的,结构持久化但数据临时。需要明确说明这些差异。接下来,定义临时表的步骤。根据引用中的例子,SQL Server直接在存储过程使用CREATETABLE #t...,而Oracle可能需要动态SQL或者先检查是否存在(如引用3和4中的例子)。需要注意Oracle的临时表需要先创建结构,存储过程中只是插入数据,数据在会话结束或事务提交后自动清除。然后,使用临时表的常见场景,比如中间结果存储、复杂查询拆分、数据清洗等,可以结合引用中的例子,比如动态SQL中使用临时表处理数据(引用1),或者在存储过程中进行数据操作(引用2、4)。还要提到性能考虑,比如临时表的创建次数(引用2中的计数器)和适当的使用方法以避免资源浪费。可能还需要比较临时表和其他结构如CTE、表变量的区别(引用5提到),帮助用户理解何时选择临时表。此外,注意事项如显式删除的必要性,在SQLServer中通常不需要,而Oracle可能需要不同的处理,但用户可能关心是否需要手动清理,引用2中提到SQL Server中是否要显示删除,实验显示临时表自动删除,所以可以强调自动管理的特性。最后,生成相关问题需要考虑用户可能的后续疑问,比如优化临时表使用、不同数据库的区别、性能影响、事务中的行为等。确保问题覆盖用户可能深入的方向。需要检查是否有遗漏的步骤,比如在Oracle中使用动态SQL创建临时表是否必要,或者是否推荐预先创建。根据引用3和4,Oracle中可能需要先检查是否存在,但临时表的结构通常是预先创建好的,存储过程中只是使用,因此需要说明两种数据库的不同方法。 确保回答结构清晰,分步骤说明,并给出代码示例,同时注意事项部分要突出不同点和常见错误,比如在Oracle中忘记提交导致数据不可见,或者SQLServer中临时表的范围限制。保持语言简洁,用中文解释,避免术语混淆。</think>### 在存储过程中定义和使用临时表的步骤 #### 一、SQL Server中的实现 1. **定义临时表** 在存储过程使用`#`前缀定义本地临时表,其作用域仅限于当前会话: ```sql CREATE PROCEDURE Proc_TestTempTable AS BEGIN CREATE TABLE #t202310 ( col1 VARCHAR(100), col2 VARCHAR(100) ); INSERT INTO #t202310 VALUES ('value1', 'value2'); SELECT * FROM #t202310; END ``` 临时表会在存储过程执行结束后自动销毁[^2]。 2. **性能优化** - 通过`sys.dm_os_performance_counters`监控临时表创建频率,避免频繁创建[^2]。 - 对于复杂查询,可结合索引提升查询效率[^5]。 --- #### 二、Oracle中的实现 1. **预定义临时表结构** 需先创建全局临时表(数据临时,结构持久): ```sql CREATE GLOBAL TEMPORARY TABLE temp_table ( id NUMBER, name VARCHAR2(50) ) ON COMMIT DELETE ROWS; -- 事务级临时数据 ``` 2. **在存储过程使用** 通过动态SQL检查表是否存在并操作数据: ```sql CREATE PROCEDURE Proc_UseTempTable IS v_count NUMBER; BEGIN SELECT COUNT(*) INTO v_count FROM user_tables WHERE table_name = 'TEMP_TABLE'; IF v_count = 0 THEN EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE temp_table (...)'; END IF; INSERT INTO temp_table VALUES (1, 'Oracle'); END; ``` 数据在事务提交或会话结束时自动清除[^3][^4]。 --- #### 三、通用使用场景 1. **中间结果存储** 用于分步处理复杂逻辑,如数据清洗或计算中间值[^1]。 2. **动态SQL支持** 在拼接动态查询时,临时表可保存中间结果集[^4]。 3. **跨作用域数据共享** 在嵌套存储过程或事务中传递数据[^5]。 --- #### 四、注意事项 - **作用域差异** SQL Server的`#temp`表仅限当前会话,Oracle临时表数据按事务/会话级别清除。 - **性能开销** 频繁创建临时表可能影响性能,建议复用表结构[^2]。 - **事务一致性** Oracle中若使用`ON COMMIT DELETE ROWS`,需注意事务提交后数据消失[^3]。 --- ### 代码示例(SQL Server) ```sql CREATE PROCEDURE ProcessSalesData AS BEGIN -- 创建临时表存储中间结果 CREATE TABLE #SalesSummary ( ProductID INT, TotalSales DECIMAL(18,2) ); -- 插入聚合数据 INSERT INTO #SalesSummary SELECT ProductID, SUM(Amount) FROM Sales GROUP BY ProductID; -- 使用临时表生成报表 SELECT p.Name, s.TotalSales FROM #SalesSummary s JOIN Products p ON s.ProductID = p.ID; END ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值