В чем разница между временной таблицей и табличной переменной в SQL Server?

В SQL Server 2005 мы можем создавать временные таблицы одним из двух способов:

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

or

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

В чем разница между этими двумя? Я читал противоречивые мнения о том, использует ли @tmp по-прежнему tempdb или все происходит в памяти.

В каких сценариях одно превосходит другое?


person Eric Z Beard    schedule 26.08.2008    source источник
comment
Также см. мой ответ здесь   -  person Martin Smith    schedule 12.04.2012
comment
Здесь действительно хорошая запись Пинала Дэйва ... blog.sqlauthority.com/2009/12/15/   -  person sam yi    schedule 03.10.2013


Ответы (12)


Между временными таблицами (#tmp) и табличными переменными (@tmp) есть несколько различий, хотя использование tempdb не входит в их число, как указано в ссылке MSDN ниже.

Как правило, для небольших и средних объемов данных и простых сценариев использования следует использовать табличные переменные. (Это слишком широкое руководство с, конечно, множеством исключений - см. Ниже и следующие статьи.)

Некоторые моменты, которые следует учитывать при выборе между ними:

  • Временные таблицы - это реальные таблицы, поэтому вы можете делать такие вещи, как CREATE INDEXes и т. Д. Если у вас есть большие объемы данных, доступ к которым по индексу будет быстрее, временные таблицы - хороший вариант.

  • Табличные переменные могут иметь индексы с использованием ограничений PRIMARY KEY или UNIQUE. (Если вы хотите, чтобы неуникальный индекс просто включал столбец первичного ключа в качестве последнего столбца в ограничении уникальности. Если у вас нет уникального столбца, вы можете использовать столбец идентификаторов.) В SQL 2014 также есть неуникальные индексы.

  • Табличные переменные не участвуют в транзакциях, а SELECTs неявно связаны с NOLOCK. Поведение транзакции может быть очень полезным, например, если вы хотите выполнить ОТКАТ на полпути через процедуру, тогда переменные таблицы, заполненные во время этой транзакции, все равно будут заполнены!

  • Временные таблицы могут часто приводить к перекомпиляции хранимых процедур. Табличных переменных не будет.

  • Вы можете создать временную таблицу с помощью SELECT INTO, которая может быть более быстрой для записи (подходит для специальных запросов) и может позволить вам иметь дело с изменением типов данных с течением времени, поскольку вам не нужно заранее определять структуру временной таблицы.

  • Вы можете передавать табличные переменные обратно из функций, что значительно упрощает инкапсуляцию и повторное использование логики (например, создание функции для разделения строки на таблицу значений с произвольным разделителем).

  • Использование табличных переменных в пользовательских функциях позволяет использовать эти функции более широко (подробности см. В документации CREATE FUNCTION). Если вы пишете функцию, вам следует использовать табличные переменные вместо временных таблиц, если нет особой необходимости.

  • Табличные переменные и временные таблицы хранятся в tempdb. Но для табличных переменных (с 2005 года) по умолчанию используется сопоставление текущей базы данных по сравнению с временными таблицами, которые принимают сопоставление по умолчанию tempdb (ref). Это означает, что вы должны знать о проблемах сопоставления, если используете временные таблицы, а сопоставление базы данных отличается от сопоставления tempdb, что вызывает проблемы, если вы хотите сравнить данные во временной таблице с данными в своей базе данных.

  • Глобальные временные таблицы (## tmp) - это еще один тип временных таблиц, доступных для всех сеансов и пользователей.

Дальнейшее чтение:

person Rory    schedule 15.09.2008
comment
Табличные переменные могут иметь индексы. Просто создайте уникальное ограничение, и вы автоматически получите индекс. Имеет огромную разницу в производительности. (Если вам не нужен уникальный индекс, просто добавьте фактический первичный ключ в конце нужных полей. Если у вас его нет, создайте столбец идентификаторов). - person Ben; 04.03.2011
comment
@Ben И SQL Server 2014 позволяет указывать неуникальные индексы для переменных таблицы - person Martin Smith; 02.07.2013
comment
Табличные переменные, на которые не влияют транзакции, иногда удобны. Если у вас есть что-то, что вы хотите сохранить после отката, вы можете поместить это в табличную переменную. - person quillbreaker; 22.10.2013
comment
Статистика создается для временных таблиц, которые могут улучшить планы запросов, но не для табличных переменных. Эти статистические данные кэшируются на некоторое время вместе со страницами временной таблицы после удаления временной таблицы и могут быть неточными, если кэшированная таблица будет повторно активирована. - person Michael Green; 27.04.2014
comment
Переменные таблицы по умолчанию будут использовать либо сопоставление пользовательского типа данных (если столбец имеет тип данных, определяемый пользователем), либо сопоставление текущей базы данных, а не сопоставление по умолчанию для базы данных tempdb. Таблицы Temp будут использовать параметры сортировки по умолчанию tempdb. См .: technet.microsoft.com/en-us/library/ms188927.aspx < / а> - person PseudoToad; 12.05.2015
comment
Это, безусловно, лучший пост / статья, которую я читал по теме временных таблиц и табличных переменных. - person Kyle Johnson; 14.12.2017
comment
Вопрос: Табличные переменные не участвуют в транзакциях, а операторы SELECT неявно имеют NOLOCK. Поведение транзакции может быть очень полезным, например, если вы хотите выполнить ОТКАТ на полпути через процедуру, тогда переменные таблицы, заполненные во время этой транзакции, все равно будут заполнены !. NO LOCK означает выбор набора данных из табличной переменной или вставку набора данных в табличную переменную? - person SKLTFZ; 16.03.2018
comment
Не знаю, отслеживается ли больше этот старый поток. Вы сказали, что временные таблицы могут привести к перекомпиляции хранимых процедур, возможно, часто. Табличных переменных не будет .. Можете уточнить на примере? - person Neil Weicher; 11.04.2018

Просто посмотрите на утверждение в принятом ответе, что переменные таблицы не участвуют в регистрации.

В целом кажется неправдой, что есть какая-либо разница в количестве журналов (по крайней мере, для _1 _ / _ 2 _ / _ 3_ операций с самой таблицей, хотя у меня есть поскольку обнаружено, что существует небольшая разница в этом отношении для кэшированных временных объектов в хранимых процедурах из-за дополнительных обновлений системной таблицы).

Я посмотрел на поведение записи в @table_variable и #temp таблицы для следующих операций.

  1. Успешная вставка
  2. Многострочная вставка, где оператор откатился из-за нарушения ограничения.
  3. Обновлять
  4. Удалить
  5. Освободить

Записи журнала транзакций были практически идентичны для всех операций.

Версия табличной переменной на самом деле имеет несколько дополнительных записей журнала, потому что она получает запись, добавляемую (а затем удаляемую) в базовую таблицу sys.syssingleobjrefs, но в целом было зарегистрировано на несколько байтов только как внутреннее имя таблицы. переменные потребляют на 236 байтов меньше, чем для #temp таблиц (на 118nvarchar символов меньше).

Полный сценарий для воспроизведения (лучше всего запускать на экземпляре, запущенном в однопользовательском режиме и использующем режим 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             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
person Martin Smith    schedule 20.11.2011
comment
+1 Просто из любопытства (и немного педантично). Вопрос был / был довольно старым (август 2008 г.), поэтому он касался SQL 2005. Сейчас мы находимся в 2011 г. (конец), и последний SQL - это 2008 R2 плюс бета-версия Denali. Какую версию вы использовали? - person xanatos; 21.11.2011
comment
@xanatos - 2008. В 2005 году табличные переменные фактически были бы в невыгодном положении, поскольку INSERT ... SELECT не регистрировалась минимально, и вы не можете SELECT INTO ... табличную переменную. - person Martin Smith; 21.11.2011
comment
Спасибо @MartinSmith, обновил свой ответ, чтобы удалить претензию о ведении журнала. - person Rory; 05.12.2015

В каких сценариях одно превосходит другое?

Для небольших таблиц (менее 1000 строк) используйте временную переменную, в противном случае используйте временную таблицу.

person SQLMenace    schedule 26.08.2008
comment
Есть какие-нибудь подтверждающие данные? Само по себе это не очень полезно. - person Michael Myers; 11.04.2012
comment
Microsoft рекомендует ограничение в 100 строк: msdn.microsoft.com/en-us/library/ ms175010.aspx (см. раздел "Рекомендации"). - person Artemix; 14.03.2013
comment
См. Объяснение в моем ответе ниже. - person Weihui Guo; 16.04.2020
comment
Это было верно в 2012 году, но в 2021 году я думаю, что порог больше похож на 100000 строк или меньше, используя табличную переменную, более того, используйте таблицу Temp (с индексом) - person Geoff Griswald; 26.04.2021

@wcm - на самом деле, чтобы придраться к переменной таблицы, это не только Ram - ее можно частично сохранить на диске.

Временная таблица может иметь индексы, тогда как табличная переменная может иметь только первичный индекс. Если скорость является проблемой. Табличные переменные могут быть быстрее, но, очевидно, если есть много записей или необходимость поиска во временной таблице кластеризованного индекса, то временная таблица будет лучше.

Хорошая справочная статья

person JamesSugrue    schedule 26.08.2008
comment
Хорошая справочная статья +1. Я удалю свой ответ, так как его изменение мало что оставит, и уже есть так много хороших ответов - person wcm; 19.10.2010

  1. Таблица Temp: Таблица Temp проста в создании и резервном копировании данных.

    Табличная переменная: Но табличная переменная требует усилий, когда мы обычно создаем обычные таблицы.

  2. Таблица Temp: Результат таблицы Temp может использоваться несколькими пользователями.

    Табличная переменная: Но табличная переменная может использоваться только текущим пользователем.

  3. Временная таблица: временная таблица будет храниться в базе данных tempdb. Это сделает сетевой трафик. Когда у нас есть большие данные во временной таблице, они должны работать в базе данных. Проблема с производительностью будет существовать.

    Табличная переменная: но табличная переменная будет храниться в физической памяти для некоторых данных, а затем, когда размер увеличится, она будет перемещена в базу данных tempdb.

  4. Временная таблица: временная таблица может выполнять все операции DDL. Он позволяет создавать индексы, удалять, изменять и т. Д.,

    Табличная переменная: Табличная переменная не позволяет выполнять операции DDL. Но табличная переменная позволяет нам создавать только кластерный индекс.

  5. Таблица Temp: Таблица Temp может использоваться для текущего сеанса или глобального. Так что многопользовательский сеанс может использовать результаты в таблице.

    Табличная переменная: Но табличная переменная может использоваться до этой программы. (Хранимая процедура)

  6. Таблица Temp: переменная Temp не может использовать транзакции. Когда мы выполняем операции DML с временной таблицей, это может быть откат или фиксация транзакций.

    Табличная переменная: Но мы не можем сделать это для табличной переменной.

  7. Временная таблица: функции не могут использовать временную переменную. Более того, мы не можем выполнять операцию DML в функциях.

    Табличная переменная: но функция позволяет нам использовать табличную переменную. Но с помощью табличной переменной мы можем это сделать.

  8. Таблица Temp: хранимая процедура выполнит перекомпиляцию (не может использовать тот же план выполнения), когда мы будем использовать переменную temp для каждого последующего вызова.

    Переменная таблицы: В то время как переменная таблицы не делает этого.

person Kumar Manish    schedule 30.04.2013

Для всех, кто верит в миф о том, что временные переменные находятся только в памяти

Во-первых, переменная таблицы НЕ обязательно находится в памяти. При нехватке памяти страницы, принадлежащие табличной переменной, могут быть перенесены в базу данных tempdb.

Прочтите статью здесь: TempDB :: Табличная переменная и локальная временная таблица

person SQLMenace    schedule 26.08.2008
comment
Можете ли вы преобразовать свои ответы в один ответ, касающийся двух пунктов? - person Joshua Drake; 01.02.2013

Другое главное отличие состоит в том, что у табличных переменных нет статистики по столбцам, в отличие от временных таблиц. Это означает, что оптимизатор запросов не знает, сколько строк находится в табличной переменной (он предполагает 1), что может привести к созданию очень неоптимальных планов, если табличная переменная действительно имеет большое количество строк.

person GilaMonster    schedule 15.09.2008
comment
Столбец rows в sys.partitions поддерживается для табличных переменных, поэтому он действительно знает, сколько строк находится в таблице. Это можно увидеть, используя OPTION (RECOMPILE). Но отсутствие статистики столбца означает, что он не может оценить конкретные предикаты столбца. - person Martin Smith; 20.11.2011

Цитата взята из; Professional SQL Server 2012 Внутреннее устройство и устранение неполадок

Статистика Основное различие между временными таблицами и табличными переменными заключается в том, что статистика не создается для табличных переменных. Это имеет два основных последствия, первое из которых состоит в том, что оптимизатор запросов использует фиксированную оценку количества строк в табличной переменной независимо от содержащихся в ней данных. Более того, добавление или удаление данных не влияет на оценку.

Индексы. Вы не можете создавать индексы для табличных переменных, хотя можете создавать ограничения. Это означает, что, создавая первичные ключи или уникальные ограничения, вы можете иметь индексы (поскольку они созданы для поддержки ограничений) для табличных переменных. Даже если у вас есть ограничения и, следовательно, индексы, которые будут иметь статистику, индексы не будут использоваться при компиляции запроса, потому что они не будут существовать во время компиляции и не вызовут перекомпиляции.

Модификации схемы. Изменения схемы возможны для временных таблиц, но не для табличных переменных. Хотя для временных таблиц возможны модификации схемы, избегайте их использования, поскольку они вызывают перекомпиляцию операторов, использующих таблицы.

Временные таблицы против табличных переменных

ТАБЛИЧНЫЕ ПЕРЕМЕННЫЕ НЕ СОЗДАЮТСЯ В ПАМЯТИ

Существует распространенное заблуждение, что переменные таблицы являются структурами в памяти и поэтому работают быстрее, чем временные таблицы. Благодаря DMV под названием sys. dm _ db _ session _ space _ usage, который показывает использование tempdb для каждого сеанса, вы можете доказать, что это не так. После перезапуска SQL Server для очистки DMV запустите следующий сценарий, чтобы убедиться, что ваш идентификатор сеанса возвращает 0 для user _ objects _ alloc _ page _ count:

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 ;

Какой из них использовать?

Независимо от того, используете ли вы временные таблицы или табличные переменные, следует решить путем тщательного тестирования, но лучше всего использовать временные таблицы по умолчанию, потому что гораздо меньше вещей, которые можно выполнить неправильно.

Я видел, как заказчики разрабатывали код с использованием табличных переменных, потому что они имели дело с небольшим количеством строк, и это было быстрее, чем временная таблица, но несколько лет спустя в табличной переменной были сотни тысяч строк, и производительность была ужасной. , так что постарайтесь предусмотреть некоторое планирование мощности, когда будете принимать решение!

person Teoman shipahi    schedule 04.09.2015
comment
Фактически статистика создается для табличных переменных, см. stackoverflow.com/questions/42824366/ - person YuFeng Shen; 16.03.2017

Еще одно отличие:

К таблице var можно получить доступ только из операторов внутри процедуры, которая ее создает, но не из других процедур, вызываемых этой процедурой или вложенным динамическим SQL (через exec или sp_executesql).

С другой стороны, область действия временной таблицы включает код в вызываемых процедурах и вложенный динамический SQL.

Если таблица, созданная вашей процедурой, должна быть доступна из других вызываемых процедур или динамического SQL, вы должны использовать временную таблицу. Это может быть очень удобно в сложных ситуациях.

person BrianFinkel    schedule 30.09.2011

Различия между Temporary Tables (##temp/#temp) и Table Variables (@table) заключаются в следующем:

  1. Table variable (@table) создается в memory. Принимая во внимание, что Temporary table (##temp/#temp) создается в tempdb database. Однако при нехватке памяти страницы, принадлежащие табличной переменной, могут быть помещены в базу данных tempdb.

  2. Table variables не может участвовать в transactions, logging or locking. Это делает @table faster then #temp. Таким образом, табличная переменная работает быстрее, чем временная таблица.

  3. Temporary table позволяет изменять схему, в отличие от Table variables.

  4. Temporary tables видны в созданной подпрограмме, а также в дочерних подпрограммах. Принимая во внимание, что переменные таблицы видны только в созданной подпрограмме.

  5. Temporary tables разрешены CREATE INDEXes тогда как Table variables не разрешены CREATE INDEX вместо этого они могут иметь индекс с помощью Primary Key or Unique Constraint.

person Litisqe Kumar    schedule 15.11.2019
comment
Табличная переменная не создается в памяти, она хранится в tempdb - person Pratik Bhattacharya; 05.08.2020
comment
@PratikBhattacharya - В MS SQL 2014 был введен специальный тип табличных переменных, оптимизированных для памяти. И они не используют tempdb. Ссылка - docs.microsoft.com/en-us/sql/relational-databases/ - person Litisqe Kumar; 10.08.2020

Меня удивляет, что никто не упомянул, что ключевое различие между ними состоит в том, что временная таблица поддерживает параллельную вставку, а переменная таблицы - нет. Вы должны увидеть отличие от плана выполнения. А вот видео с семинаров по SQL на канале 9.

Это также объясняет, почему вы должны использовать табличную переменную для небольших таблиц, в противном случае используйте временную таблицу, как SQLMenace ответил ранее.

person Weihui Guo    schedule 13.02.2020

Учтите также, что вы часто можете заменить обе производными таблицами, которые также могут быть быстрее. Однако, как и при любой настройке производительности, только фактические тесты с вашими фактическими данными могут подсказать вам лучший подход для вашего конкретного запроса.

person HLGEM    schedule 15.09.2008