Яка різниця між тимчасовою таблицею та змінною таблиці в SQL Server?


390

У SQL Server 2005 ми можемо створити темп-таблиці одним із двох способів:

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

або

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

Які відмінності між цими двома? Я читав суперечливі думки щодо того, чи все ще @tmp використовує tempdb, чи все відбувається в пам'яті.

У яких сценаріях один переважає інший?



2
Тут по-справжньому добре писав Pinal Dave ... blog.sqlauthority.com/2009/12/15/…
сам-й,

Відповіді:


392

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

Як правило, для малого та середнього обсягу даних та простих сценаріїв використання слід використовувати змінні таблиці. (Це занадто широке керівництво, звичайно, безліч винятків - див. Нижче та наступні статті.)

Деякі моменти, які слід враховувати при виборі між ними:

  • Тимчасові таблиці - це справжні таблиці, тому ви можете робити такі речі, як CREATE INDEXes тощо. Якщо у вас є велика кількість даних, доступ до яких за індексом буде швидшим, тимчасові таблиці - хороший варіант.

  • Змінні таблиці можуть мати індекси, використовуючи PRIMARY KEY або UNIQUE обмеження. (Якщо ви хочете, щоб унікальний індекс просто не включав стовпчик первинного ключа в якості останнього стовпчика в єдиному обмеженні. Якщо у вас немає унікального стовпця, ви можете використовувати стовпчик ідентичності.) SQL 2014 також має унікальні індекси .

  • Змінні таблиці не беруть участі в транзакціях, і SELECTs явно це стосується NOLOCK. Поведінка транзакцій може бути дуже корисною, наприклад, якщо ви хочете перейти на середину через процедуру, тоді змінні таблиці, заповнені під час транзакції, все ще будуть заповнені!

  • Таблиці темпів можуть призвести до того, що зберігаються процедури можуть бути перекомпільовані, можливо, часто. Змінні таблиці не будуть.

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

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

  • Використання табличних змінних у визначених користувачем функціях дозволяє використовувати ці функції ширше (детальніше див. Документацію CREATE FUNCTION). Якщо ви пишете функцію, ви повинні використовувати змінні таблиці для темп-таблиць, якщо інша переконлива потреба не має.

  • І змінні таблиці, і таблиці temp зберігаються у tempdb. Але змінні таблиці (з 2005 року) за замовчуванням для порівняння поточної бази даних проти таблиць temp, які приймають за замовчуванням порівняння tempdb ( ref ). Це означає, що вам слід знати про проблеми зіставлення, якщо використання темп-таблиць і db-зіставлення відрізняються від tempdb, викликаючи проблеми, якщо ви хочете порівнювати дані в темп-таблиці з даними у вашій базі даних.

  • Global Temp Tables (## tmp) - це ще один тип темп-таблиць, доступний для всіх сесій та користувачів.

Деякі подальші читання:


26
Змінні таблиці можуть мати індекси. Просто створіть унікальне обмеження, і ви автоматично отримаєте індекс. Значна різниця у продуктивності. (Якщо ви не хочете отримати унікальний індекс, просто додайте фактичний первинний ключ у кінці потрібних полів. Якщо у вас його немає, створіть стовпчик ідентичності).
Бен

7
@Ben And SQL Server 2014 дозволяє не унікальні індекси вказувати на змінних таблиці
Martin Smith

4
Іноді зручні табличні змінні, на які транзакції не впливають. Якщо у вас є все, що ви хочете зберегти після відкату, ви можете помістити його в змінну таблиці.
quillbreaker

3
Статистика створюється для тимчасових таблиць, які можуть покращити плани запитів, але не для змінних таблиць. Ця статистика кешується деякий час, разом зі сторінками таблиці темп, після того, як таблиця темпів буде скинута і може бути неточною у разі повторної активації кешованої таблиці.
Майкл Грін

Змінні таблиці за замовчуванням застосовуватимуть або зіставлення визначеного користувачем типу даних (якщо стовпець призначений користувачем типу даних), або порівняння поточної бази даних, а не порівняння tempdb за замовчуванням. Тимчасові таблиці використовуватимуть порівняння за замовчуванням tempdb. Див: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad

25

Просто дивлячись на твердження у прийнятій відповіді, що змінні таблиці не беруть участі в реєстрації.

Взагалі неправдиво, що є різниця у кількості журналів (принаймні, для insert/ update/ deleteоперацій із самою таблицею, хоча я з тих пір знайшов що в цьому відношенні є певна невелика різниця щодо кешованих тимчасових об'єктів у збережених процедурах завдяки додатковій системній таблиці оновлення).

Я переглянув поведінку ведення журналу як на a, так @table_variableі на #tempтаблицю для наступних операцій.

  1. Успішна вставка
  2. Multi Row Insert, де заява відкочується через порушення обмежень.
  3. Оновлення
  4. Видалити
  5. Розподіліть

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

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

Повний сценарій для відтворення (найкраще запускати в екземплярі, запущеному в режимі одного користувача та в 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
+1 Тільки з цікавості (і бути трохи педантичним). Питання є / було досить старим (серпень 2008 р.), Тому мова йшла про SQL 2005. Зараз ми перебуваємо у 2011 році (кінець), а останній SQL - 2008 R2 плюс бета-версія Denali. Яку версію ви використовували?
xanatos

2
@xanatos - 2008. У 2005 році змінні таблиці фактично були б у невигідному стані, оскільки їх INSERT ... SELECTне було мінімально зафіксовано, і ти не можеш змінити SELECT INTO ... таблицю.
Мартін Сміт

1
Дякую @MartinSmith, оновив мою відповідь, щоб видалити претензію на реєстрацію.
Рорі

18

У яких сценаріях один переважає інший?

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


17
Будь-які допоміжні дані? Це не дуже корисно як твердження самостійно.
Майкл Майєрс

8
Microsoft рекомендує обмежити 100 рядків: msdn.microsoft.com/en-us/library/ms175010.aspx (див. Розділ «Кращі практики»).
Артемікс

Дивіться мою відповідь нижче для пояснення.
Вейхуй Гоо

17

@wcm - насправді, щоб ніт вибрати табличну змінну - це не лише рамка - її можна частково зберігати на диску.

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

Хороша довідкова стаття


2
Гарна довідкова стаття +1. Я буду видаляти свою відповідь, оскільки змінити її не залишить багато, і вже так багато хороших відповідей
wcm,

12
  1. Таблиця темпів: Таблицю темпів легко створювати та створювати резервні копії даних.

    Таблична змінна: Але змінна таблиці передбачає зусилля, коли ми зазвичай створюємо звичайні таблиці.

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

    Таблична змінна: Але змінну таблиці можна використовувати лише поточного користувача. 

  3. Таблиця темпів: Таблиця темпів зберігатиметься у tempdb. Це зробить мережевий трафік. Коли у нас є велика кількість даних у темп-таблиці, вона повинна працювати в базі даних. Проблема з продуктивністю буде існувати.

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

  4. Таблиця темпів: Таблиця темпів може виконувати всі операції DDL. Це дозволяє створювати індекси, випадати, змінювати тощо.,

    Змінна таблиця: В той час як змінна таблиця не дозволить робити операції DDL. Але змінна таблиці дозволяє нам створювати лише кластерний індекс.

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

    Таблична змінна: Але змінну таблиці можна використовувати до цієї програми. (Збережена процедура)

  6. Таблиця темпів: змінна Temp не може використовувати транзакції. Коли ми робимо операції DML з таблицею temp, тоді вона може бути відкатаною або здійснювати транзакції.

    Таблична змінна: Але ми не можемо зробити це для змінної таблиці.

  7. Таблиця темпів: Функції не можуть використовувати змінну temp. Більше того, ми не можемо робити операції DML у функціях.

    Таблична змінна: Але функція дозволяє використовувати табличну змінну. Але за допомогою змінної таблиці ми можемо це зробити.

  8. Таблиця темпів: Збережена процедура виконає рекомпіляцію (не може використовувати той самий план виконання), коли ми використовуємо змінну temp для всіх подальших послідовних викликів.

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


8

Для всіх вас, хто вірить міфу, що тимчасові змінні є лише в пам'яті

По-перше, змінна таблиці НЕ обов'язково є резидентом пам'яті. Під тиском пам’яті сторінки, що належать до змінної таблиці, можуть витіснятись до tempdb.

Прочитайте статтю тут: TempDB :: Змінна таблиця та локальна тимчасова таблиця


3
Чи можете ви редагувати свої відповіді в одну відповідь, що стосується двох точок?
Джошуа Дрейк

7

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


2
rowsСтовпець sys.partitionsпідтримується для таблиці змінних , так що на насправді знає , скільки рядків у таблиці. Це можна побачити, використовуючи OPTION (RECOMPILE). Але відсутність статистики стовпців означає, що він не може оцінити конкретні предикати стовпців.
Мартін Сміт

7

Цитата, взята з; Професійні внутрішні системи та усунення несправностей у SQL Server 2012

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

Індекси Не можна створювати індекси на змінних таблиці, хоча ви можете створювати обмеження. Це означає, що, створюючи первинні ключі або унікальні обмеження, ви можете мати індекси (оскільки вони створені для підтримки обмежень) на змінних таблиці. Навіть якщо у вас є обмеження, а отже, індекси, які матимуть статистику, індекси не використовуватимуться при складанні запиту, оскільки вони не існуватимуть під час компіляції, а також не спричинять рекомпіляції.

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

Тимчасові таблиці проти табличних змінних

ТАБЛИЧНІ ВАРІАБЛІ НЕ СТВОРЕНІ В ПАМ'ЯТІ

Існує поширене неправильне уявлення про те, що змінні таблиці є структурами пам'яті, і як такі вони будуть працювати швидше, ніж тимчасові таблиці . Завдяки DMV під назвою sys. dm _ db _ сесія _ простір _ використання, яке показує використання tempdb за сеансом, ви можете довести, що це не так . Після перезапуску SQL Server для очищення DMV запустіть наступний скрипт, щоб переконатися, що ваш сеанс _ id повертає 0 для користувача _ об’єктів _ alloc _ сторінки _ кількість:

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 ;

Який використовувати?

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

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


Насправді статистика створюється за табличними змінними, див. Stackoverflow.com/questions/42824366/…
YuFeng Shen

4

Ще одна відмінність:

До змінної таблиці можна звертатися лише з операторів у процедурі, яка її створює, а не з інших процедур, викликаних цією процедурою або вкладених динамічним SQL (через exec або sp_executesql).

Об'єм таблиці темп, з іншого боку, включає код у названих процедурах та вкладений динамічний SQL.

Якщо таблиця, створена вашою процедурою, повинна бути доступною з інших викликаних процедур або динамічного SQL, ви повинні використовувати таблицю темп. Це може бути дуже зручно у складних ситуаціях.


2

Відмінності між Temporary Tables (##temp/#temp)і Table Variables (@table)такі:

  1. Table variable (@table)створюється в memory. В той час, як A 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.


1

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


1

Мене дивує, що ніхто не згадував про ключову різницю між цими двома в тому, що таблиця темп підтримує паралельну вставку, тоді як змінну таблиці немає. Ви повинні мати можливість бачити відмінність від плану виконання. А ось відео з SQL Workshop на 9 каналі .

Це також пояснює, чому слід використовувати змінну temp для менших таблиць, інакше використовувати таблицю temp, як SQLMenace відповів раніше.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.