临时表与表变量
临时表
可以创建本地临时表和全局临时表。本地临时表仅在当前会话中可见,而全局临时表在所有会话中都可见。临时表不能分区。
本地临时表的名称前面有一个数字符号 (#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 约束中不能引用临时表。
建议您使用表变量而不使用临时表。当必须对临时表显式地创建索引时,或多个存储过程或函数必须使用表值时,临时表很有用。通常,表变量可提供更有效的查询处理。有关详细信息,请参阅表 (Transact-SQL)。
-----------
可将函数和变量声明为 table 类型。table 变量可用于函数、存储过程和批处理中。
![]() |
---|
修改 table 变量的查询不会生成并行查询执行计划。修改特大型 table 变量或复杂查询中的 table 变量时,可能会影响性能。在这种情况下,请考虑改用临时表。有关详细信息,请参阅 CREATE TABLE (Transact-SQL)。还可以并行执行读取 table 变量而不对变量进行修改的查询。 |
table 变量具有下列优点:
- A. table 变量的行为类似于局部变量,有明确定义的作用域。这就是在其中声明该变量的函数、存储过程或批处理。
在其作用域内,table 变量可像常规表那样使用。该变量可应用于 SELECT、INSERT、UPDATE 和 DELETE 语句中用到表或表的表达式的任何地方。但是,table 不能用于以下语句中:
SELECT select_list INTO table_variable
- table 类型声明中的 CHECK 约束、DEFAULT 值和计算列不能调用用户定义函数。
- 在存储过程中使用 table 变量与使用临时表相比,减少了存储过程的重新编译量。
- 涉及 table 变量的事务只在 table 变量更新期间存在。因此减少了 table 变量对锁定和记录资源的需求。
不能显式创建 table 变量的索引,也不保留 table 变量的任何统计信息。在某些情况下,可以通过改用支持索引和统计信息的临时表来改善性能。有关临时表的详细信息,请参阅 CREATE TABLE (Transact-SQL)。
可以在批处理的 FROM 子句中按名称引用 table 变量,如下例所示:
SELECT Employee_ID, Department_ID FROM @MyTableVar
在 FROM 子句外,必须使用别名来引用 table 变量,如下例所示:
SELECT EmployeeID, DepartmentID FROM @MyTableVar m JOIN Employee on (m.EmployeeID =Employee.EmployeeID AND m.DepartmentID = Employee.DepartmentID)
不支持在 table 变量之间进行赋值操作。另外,由于 table 变量作用域有限,并且不是持久数据库的一部分,因而不受事务回滚的影响。
- @ table_variable_name
-
table 类型的变量的名称。变量名称必须以 at 符 (@) 开头,并符合有关标识符的规则。
- <table_type_definition>
-
定义 table 数据类型。表声明包括列定义、名称、数据类型和约束。允许的约束类型只包括 PRIMARY KEY、UNIQUE、NULL 和 CHECK。
下例将创建一个 table
变量,用于储存 UPDATE 语句的 OUTPUT 子句中指定的值。在它后面的两个 SELECT
语句返回 @MyTableVar
中的值以及 Employee
表中更新操作的结果。请注意,INSERTED.ModifiedDate
列中的结果与 Employee
表的 ModifiedDate
列中的值不同。这是因为对 Employee
表定义了 AFTER UPDATE
触发器,该触发器可以将 ModifiedDate
的值更新为当前日期。不过,从 OUTPUT
返回的列将反映触发器激发之前的数据。有关详细信息,请参阅 OUTPUT 子句 (Transact-SQL)。
USE AdventureWorks; GO DECLARE @MyTableVar table( EmpID int NOT NULL, OldVacationHours int, NewVacationHours int, ModifiedDate datetime); UPDATE TOP (10) HumanResources.Employee SET VacationHours = VacationHours * 1.25 OUTPUT INSERTED.EmployeeID, DELETED.VacationHours, INSERTED.VacationHours, INSERTED.ModifiedDate INTO @MyTableVar; --Display the result set of the table variable. SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate FROM @MyTableVar; GO --Display the result set of the table. --Note that ModifiedDate reflects the value generated by an --AFTER UPDATE trigger. SELECT TOP (10) EmployeeID, VacationHours, ModifiedDate FROM HumanResources.Employee; GO