SQL Server 中的临时表和表变量有啥区别?

Posted

技术标签:

【中文标题】SQL Server 中的临时表和表变量有啥区别?【英文标题】:What's the difference between a temp table and table variable in SQL Server?SQL Server 中的临时表和表变量有什么区别? 【发布时间】:2010-09-06 20:57:47 【问题描述】:

在 SQL Server 2005 中,我们可以通过以下两种方式之一创建临时表:

declare @tmp table (Col1 int, Col2 int);

create table #tmp (Col1 int, Col2 int);

这两者有什么区别?关于@tmp 是否仍然使用 tempdb,或者一切都发生在内存中,我已经阅读了相互矛盾的意见。

在哪些情况下,其中一种表现优于另一种?

【问题讨论】:

Also see my answer here 这里的 Pinal Dave 写得非常好...blog.sqlauthority.com/2009/12/15/… 【参考方案1】:

@wcm - 实际上挑选表变量不仅仅是 Ram - 它可以部分存储在磁盘上。

临时表可以有索引,而表变量只能有主索引。如果速度是个问题,表变量可以更快,但显然如果记录很多,或者需要搜索聚集索引的临时表,那么临时表会更好。

Good background article

【讨论】:

好背景文章+1。我将删除我的答案,因为修改它不会留下太多,而且已经有很多好的答案【参考方案2】:

对于所有相信临时变量只存在于内存中的神话的人

首先,表变量不一定是内存驻留的。在内存压力下,属于表变量的页面可以被推送到 tempdb。

在这里阅读文章:TempDB:: Table variable vs local temporary table

【讨论】:

您能否将您的答案编辑成一个针对这两点的答案?【参考方案3】:

在哪些情况下,其中一种表现优于另一种?

对于较小的表(少于 1000 行)使用临时变量,否则使用临时表。

【讨论】:

任何支持数据?这仅作为一个断言本身并没有多大帮助。 Microsoft 建议限制为 100 行:msdn.microsoft.com/en-us/library/ms175010.aspx(请参阅最佳实践部分)。 查看我的answer下面的解释。 这在 2012 年是正确的,但在 2021 年我认为阈值更像是 100,000 行或更少使用表变量,更多的是使用临时表(带索引)【参考方案4】:

临时表 (#tmp) 和表变量 (@tmp) 之间存在一些差异,尽管使用 tempdb 不是其中之一,如下面的 MSDN 链接中所述。

根据经验,对于中小型数据量和简单的使用场景,您应该使用表变量。 (这是一个过于宽泛的指南,当然也有很多例外 - 请参阅下文和后续文章。)

在它们之间进行选择时要考虑的几点:

临时表是真实表,因此您可以执行 CREATE INDEX 等操作。如果您有大量数据,通过索引访问会更快,那么临时表是一个不错的选择。

表变量可以通过使用 PRIMARY KEY 或 UNIQUE 约束来拥有索引。 (如果您想要一个非唯一索引,只需将主键列作为唯一约束中的最后一列。如果您没有唯一列,则可以使用标识列。)SQL 2014 has non-unique indexes too。

表变量不参与事务,SELECTs 隐含与NOLOCK。事务行为可能非常有用,例如,如果您想在过程中途回滚,那么在该事务期间填充的表变量仍将被填充!

临时表可能会导致重新编译存储过程,这可能是经常发生的。表变量不会。

您可以使用 SELECT INTO 创建一个临时表,这样可以更快地编写(适用于临时查询),并且可以让您处理随时间变化的数据类型,因为您不需要定义预先设置您的临时表结构。

您可以将表变量从函数传回,使您能够更轻松地封装和重用逻辑(例如,使函数将字符串拆分为任意分隔符上的值表)。

李>

在用户定义的函数中使用表变量可以更广泛地使用这些函数(有关详细信息,请参阅 CREATE FUNCTION 文档)。如果您正在编写一个函数,您应该在临时表上使用表变量,除非另有迫切需要。

表变量和临时表都存储在 tempdb 中。但是表变量(自 2005 年以来)默认为当前数据库的排序规则,而临时表则采用默认的 tempdb 排序规则 (ref)。这意味着如果使用临时表并且您的 db 排序规则与 tempdb 的不同,您应该注意排序规则问题,如果您想将临时表中的数据与数据库中的数据进行比较,则会导致问题。

全局临时表 (##tmp) 是另一种可供所有会话和用户使用的临时表。

进一步阅读:

Martin Smith's great answer 在 dba.stackexchange.com

关于两者区别的 MSDN 常见问题解答:https://support.microsoft.com/en-gb/kb/305977

MDSN 博客文章:https://docs.microsoft.com/archive/blogs/sqlserverstorageengine/tempdb-table-variable-vs-local-temporary-table

文章:https://searchsqlserver.techtarget.com/tip/Temporary-tables-in-SQL-Server-vs-table-variables

临时表和临时变量的意外行为和性能影响:Paul White on SQLblog.com

【讨论】:

表变量可以有索引。只需创建一个唯一约束,您就会自动获得一个索引。产生巨大的性能差异。 (如果您不想要唯一索引,只需在所需字段的末尾添加实际主键即可。如果您没有,请创建一个标识列)。 @Ben And SQL Server 2014 允许非唯一索引to be specified on table variables 不受事务影响的表变量有时很方便。如果您在回滚后想要保留任何内容,可以将其放入表变量中。 统计是为临时表创建的,这可以使查询计划更好,但不适用于表变量。在删除临时表后,这些统计信息会与临时表的页面一起缓存一段时间,如果重新激活缓存的表,这些统计信息可能会不准确。 表变量将默认为用户定义的数据类型的排序规则(如果列是用户定义的数据类型)或当前数据库的排序规则,而不是默认的排序规则临时数据库。临时表将使用 tempdb 默认排序规则。见:technet.microsoft.com/en-us/library/ms188927.aspx【参考方案5】:

还请考虑,您通常可以用派生表替换两者,这也可能更快。但是,与所有性能调整一样,只有针对您的实际数据的实际测试才能告诉您特定查询的最佳方法。

【讨论】:

【参考方案6】:

另一个主要区别是表变量没有列统计信息,而临时表有。这意味着查询优化器不知道表变量中有多少行(它猜测为 1),如果表变量实际上有大量行,这可能会导致生成高度非最优的计划。

【讨论】:

sys.partitions 中的rows 列是为表变量维护的,因此它确实知道表中有多少行。这可以通过使用OPTION (RECOMPILE) 来查看。但是缺少列统计信息意味着它无法估计特定的列谓词。【参考方案7】:

另一个区别:

只能从创建它的过程中的语句访问表 var,而不能从该过程调用的其他过程或嵌套的动态 SQL(通过 exec 或 sp_executesql)访问。

另一方面,临时表的范围包括调用过程和嵌套动态 SQL 中的代码。

如果您的过程创建的表必须可以从其他调用的过程或动态 SQL 访问,则必须使用临时表。这在复杂情况下非常方便。

【讨论】:

【参考方案8】:

只需查看已接受答案中的声明,即表变量不参与日志记录。

一般来说,记录的数量有任何差异似乎是不真实的(至少对于表本身的insert/update/delete 操作,尽管我有since found,这有一些小的差异由于额外的系统表更新,请尊重存储过程中缓存的临时对象)。

我查看了针对 @table_variable#temp 表的日志记录行为以进行以下操作。

    插入成功 多行插入 where 语句由于违反约束而回滚。 更新 删除 解除分配

所有操作的事务日志记录几乎相同。

表变量版本实际上有一些 额外 日志条目,因为它获得了一个添加到(然后从)sys.syssingleobjrefs 基表中删除的条目,但总体上记录的字节数较少,纯粹为表变量的内部名称比 #temp 表少 236 个字节(nvarchar 少 118 个字符)。

要重现的完整脚本(最好在以单用户模式启动并使用sqlcmd 模式的实例上运行)

:setvar tablename "@T" 
:setvar tablescript "DECLARE @T TABLE"

/*
 --Uncomment this section to test a #temp table
:setvar tablename "#T" 
:setvar tablescript "CREATE TABLE #T"
*/

USE tempdb 
GO    
CHECKPOINT

DECLARE @LSN NVARCHAR(25)

SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null) 


EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT

$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)


BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT

INSERT INTO $(tablename)
DEFAULT VALUES

BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT


INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns

BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT


/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH

BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT

UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
    OffRowFiller  =LOWER(OffRowFiller),
    LOBFiller  =LOWER(LOBFiller)


BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT

DELETE FROM  $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT

BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')


DECLARE @LSN_HEX NVARCHAR(25) = 
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)        

SELECT 
    [Operation],
    [Context],
    [AllocUnitName],
    [Transaction Name],
    [Description]
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  

SELECT CASE
         WHEN GROUPING(Operation) = 1 THEN 'Total'
         ELSE Operation
       END AS Operation,
       Context,
       AllocUnitName,
       COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
       COUNT(*)                              AS Cnt
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())

结果

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @TV      |             #TV      |                  |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation             | Context            | AllocUnitName             | Size in Bytes | Cnt  | Size in Bytes | Cnt  | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT        | LCX_NULL           |                           | 52            | 1    | 52            | 1    |                  |
| LOP_BEGIN_XACT        | LCX_NULL           |                           | 6056          | 50   | 6056          | 50   |                  |
| LOP_COMMIT_XACT       | LCX_NULL           |                           | 2548          | 49   | 2548          | 49   |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 624           | 3    | 624           | 3    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 208           | 1    | 208           | 1    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrscols.clst        | 832           | 4    | 832           | 4    |                  |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           |                           | 120           | 3    | 120           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 720           | 9    | 720           | 9    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.clust   | 444           | 3    | 444           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.nc      | 276           | 3    | 276           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.clst       | 628           | 4    | 628           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.nc         | 484           | 4    | 484           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.clst      | 176           | 1    | 176           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.nc        | 144           | 1    | 144           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.clst        | 100           | 1    | 100           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.nc1         | 88            | 1    | 88            | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysobjvalues.clst     | 596           | 5    | 596           | 5    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrowsets.clust      | 132           | 1    | 132           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrscols.clst        | 528           | 4    | 528           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.clst       | 1040          | 6    | 1276          | 6    | 236              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc1        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc2        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc3        | 480           | 6    | 480           | 6    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.clst | 96            | 1    |               |      | -96              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.nc1  | 88            | 1    |               |      | -88              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | Unknown Alloc Unit        | 72092         | 19   | 72092         | 19   |                  |
| LOP_DELETE_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 16348         | 37   | 16348         | 37   |                  |
| LOP_FORMAT_PAGE       | LCX_HEAP           | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit        | 252           | 3    | 252           | 3    |                  |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 84            | 1    | 84            | 1    |                  |
| LOP_FORMAT_PAGE       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 4788          | 57   | 4788          | 57   |                  |
| LOP_HOBT_DDL          | LCX_NULL           |                           | 108           | 3    | 108           | 3    |                  |
| LOP_HOBT_DELTA        | LCX_NULL           |                           | 9600          | 150  | 9600          | 150  |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 456           | 3    | 456           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syscolpars.clst       | 644           | 4    | 644           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysidxstats.clst      | 180           | 1    | 180           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysiscols.clst        | 104           | 1    | 104           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysobjvalues.clst     | 616           | 5    | 616           | 5    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 136           | 1    | 136           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrscols.clst        | 544           | 4    | 544           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1064          | 6    | 1300          | 6    | 236              |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syssingleobjrefs.clst | 100           | 1    |               |      | -100             |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | Unknown Alloc Unit        | 135888        | 19   | 135888        | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysallocunits.nc      | 288           | 3    | 288           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syscolpars.nc         | 500           | 4    | 500           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysidxstats.nc        | 148           | 1    | 148           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysiscols.nc1         | 92            | 1    | 92            | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc1        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc2        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc3        | 504           | 6    | 504           | 6    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syssingleobjrefs.nc1  | 92            | 1    |               |      | -92              |
| LOP_INSERT_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 5112          | 71   | 5112          | 71   |                  |
| LOP_MARK_SAVEPOINT    | LCX_NULL           |                           | 508           | 8    | 508           | 8    |                  |
| LOP_MODIFY_COLUMNS    | LCX_CLUSTERED      | Unknown Alloc Unit        | 1560          | 10   | 1560          | 10   |                  |
| LOP_MODIFY_HEADER     | LCX_HEAP           | Unknown Alloc Unit        | 3780          | 45   | 3780          | 45   |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.syscolpars.clst       | 384           | 4    | 384           | 4    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysidxstats.clst      | 100           | 1    | 100           | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysrowsets.clust      | 92            | 1    | 92            | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1144          | 13   | 1144          | 13   |                  |
| LOP_MODIFY_ROW        | LCX_IAM            | Unknown Alloc Unit        | 4224          | 48   | 4224          | 48   |                  |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit        | 13632         | 169  | 13632         | 169  |                  |
| LOP_MODIFY_ROW        | LCX_TEXT_MIX       | Unknown Alloc Unit        | 108640        | 120  | 108640        | 120  |                  |
| LOP_ROOT_CHANGE       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 960           | 10   | 960           | 10   |                  |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit        | 1200          | 20   | 1200          | 20   |                  |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit        | 1080          | 18   | 1080          | 18   |                  |
| LOP_SET_BITS          | LCX_SGAM           | Unknown Alloc Unit        | 120           | 2    | 120           | 2    |                  |
| LOP_SHRINK_NOOP       | LCX_NULL           |                           |               |      | 32            | 1    | 32               |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total                 |                    |                           | 410144        | 1095 | 411232        | 1092 | 1088             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

【讨论】:

+1 只是出于好奇(有点迂腐)。问题是/已经很老了(2008 年 8 月),所以它是关于 SQL 2005。现在我们在 2011 年(结束),最新的 SQL 是 2008 R2 加上 Denali 测试版。你用的是什么版本? @xanatos - 2008 年。在 2005 年,表变量实际上处于劣势,因为 INSERT ... SELECT 没有最低限度地记录,您不能 SELECT INTO ... 表变量。 感谢@MartinSmith,更新了我的答案以删除有关日志记录的声明。【参考方案9】:

    临时表:临时表易于创建和备份数据。

    表变量:但是表变量涉及到我们通常创建普通表时的工作量。

    临时表:临时表结果可供多个用户使用。

    表变量:但表变量只能由当前用户使用。

    临时表:临时表将存储在 tempdb 中。它会产生网络流量。当我们在临时表中有大量数据时,它必须跨数据库工作。将存在性能问题。

    表变量:但是表变量会在物理内存中存储一​​些数据,然后当大小增加时,它会被移动到 tempdb。

    临时表:临时表可以做所有的DDL操作。它允许创建索引、删除、更改等,

    表变量:而表变量不允许进行 DDL 操作。但是表变量只允许我们创建聚集索引。

    临时表:临时表可用于当前会话或全局。这样一个多用户会话就可以利用表中的结果。

    表变量:但表变量可以用于该程序。 (存储过程)

    临时表:临时变量不能使用事务。当我们对临时表进行 DML 操作时,它可以回滚或提交事务。

    表变量:但我们不能对表变量这样做。

    临时表:函数不能使用临时变量。此外,我们不能在函数中进行 DML 操作。

    表变量:但函数允许我们使用表变量。但是使用 table 变量我们可以做到这一点。

    临时表:当我们为每个后续调用使用临时变量时,存储过程将进行重新编译(不能使用相同的执行计划)。

    表变量:而表变量不会那样做。

【讨论】:

【参考方案10】:

引自; Professional SQL Server 2012 Internals and Troubleshooting

统计数据 临时表和表变量之间的主要区别在于 不会在表变量上创建统计信息。这有两个主要 结果,首先是查询优化器使用 固定估计表变量中的行数 与它包含的数据无关。此外,添加或删除 数据不会改变估计。

索引您不能在表变量上创建索引,尽管您可以 创建约束。这意味着通过创建主键或唯一 约束,你可以有索引(因为这些是为了支持 约束)对表变量。即使你有限制,并且 因此将有统计信息的索引,索引不会 在编译查询时使用,因为它们在编译时不存在 时间,也不会导致重新编译。

架构修改可以临时修改架构 表,但不在表变量上。虽然模式修改是 可能在临时表上,避免使用它们,因为它们会导致 重新编译使用这些表的语句。

表变量不在内存中创建

有一个常见的误解,即表变量是内存中的结构 因此会比临时表执行得更快。感谢车管所 称为 sys 。 dm _ db _ session _ space _ usage ,它显示 tempdb 的使用情况 会话,你可以证明事实并非如此。重新启动 SQL Server 以清除 DMV,运行以下脚本以确认您的 session_id 返回 0 用户_对象_分配_页面_计数:

SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

现在您可以通过运行以下命令来检查临时表使用了多少空间 用一列创建一个临时表并用一行填充它的脚本:

CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

我的服务器上的结果表明该表在 tempdb 中分配了一页。 现在运行相同的脚本,但使用表变量 这次:

DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

使用哪一个?

你是否使用临时表或表变量应该是 通过彻底的测试来决定,但最好是暂时的 默认使用表格,因为可以使用的东西要少得多 错误

我见过客户使用表变量开发代码,因为他们 正在处理少量的行,它比 临时表,但几年后有数百个 表变量中有数千行,性能很糟糕, 所以当你做你的 决定!

【讨论】:

实际上统计是在表变量上创建的,见***.com/questions/42824366/…【参考方案11】:

Temporary Tables (##temp/#temp)Table Variables (@table) 的区别如下:

    Table variable (@table)memory 中创建。而Temporary table (##temp/#temp) 是在tempdb database 中创建的。但是,如果存在内存压力,则属于表变量的页面可能会被推送到 tempdb。

    Table variables 不能参与transactions, logging or locking。这使得@table faster then #temp。所以表变量比临时表快。

    Temporary table 允许 Schema 修改,这与 Table variables 不同。

    Temporary tables 在创建的例程和子例程中可见。而表变量仅在创建的例程中可见。

    Temporary tables 允许使用CREATE INDEXes,而Table variables 不允许使用CREATE INDEX,它们可以使用Primary Key or Unique Constraint 获得索引。

【讨论】:

表变量不在内存中创建,它存储在 tempdb 中 @PratikBhattacharya - 在 MS SQL 2014 中,引入了一种特殊类型的表变量“内存优化表变量”。他们不使用 tempdb。参考 - docs.microsoft.com/en-us/sql/relational-databases/…【参考方案12】:

令我惊讶的是,没有人提到这两者之间的主要区别是临时表支持并行插入,而表变量不支持。您应该能够从执行计划中看到差异。这里是the video from SQL Workshops on Channel 9。

这也解释了为什么应该为较小的表使用表变量,否则使用临时表,如之前的SQLMenace answered。

【讨论】:

【参考方案13】:

在 SQL 中,临时表存储在 TempDB 中,本地临时表仅在当前会话中可见,在另一个会话中不可见。这可以在嵌套的存储过程调用之间共享。全局临时表对所有其他会话可见,并且在最后一个连接引用表关闭时被销毁。例如,

Select Dept.DeptName, Dept.DeptId, COUNT(*) as TotalEmployees
into #TempEmpCount
from Tbl_EmpDetails Emp
join Tbl_Dept Dept
on Emp.DeptId = Dept.DeptId
group by DeptName, Dept.DeptId

表变量与 tempTables 类似,在 TempDB 中也创建了一个表变量。表变量的范围是声明它的批处理、存储过程或语句块。它们可以作为过程之间的参数传递。可以使用 Table 变量 by 编写相同的查询

Declare @tblEmployeeCount table
(DeptName nvarchar(20),DeptId int, TotalEmployees int)
Insert @tblEmployeeCount
Select DeptName, Tbl_Dept.DeptId, COUNT(*) as TotalEmployees
from Tbl_EmpDetails
join Tbl_Dept
on Tbl_EmpDetails.DeptId = Tbl_Dept.DeptId
group by DeptName, Tbl_Dept.DeptId

【讨论】:

以上是关于SQL Server 中的临时表和表变量有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 表变量和临时表的区别

sql server中的用户临时表和全局临时表的区别

mysql 内存表和一般表的区别

TSQL--临时表和表变量

插入临时表和表变量时的结果不同

InMemory:在内存中创建临时表和表变量