Ответ 1
Ваш вопрос показывает, что вы уступили некоторым распространенным заблуждениям относительно переменных таблицы и временных таблиц.
Я написал довольно обширный ответ на сайте DBA, в котором рассматриваются различия между двумя типами объектов. Это также отвечает на ваш вопрос о дисках и памяти (я не видел существенной разницы в поведении между ними).
Что касается вопроса в заголовке, хотя относительно того, когда использовать переменную таблицы против локальной временной таблицы, у вас не всегда есть выбор. Например, в функциях можно использовать только переменную таблицы, и если вам нужно записать данные в таблицу в дочерней области, то #temp
только таблица #temp
(табличные параметры разрешают доступ только для чтения).
Там, где у вас есть выбор, ниже приведены некоторые предложения (хотя самый надежный способ - просто протестировать оба варианта с вашей конкретной рабочей нагрузкой).
-
Если вам нужен индекс, который не может быть создан для табличной переменной, тогда вам, конечно, понадобится
#temporary
таблица. Детали этого зависят от версии, однако. Для SQL Server 2012 и ниже единственными индексами, которые могли быть созданы для табличных переменных, были те, которые неявно создавались с помощью ограниченияUNIQUE
илиPRIMARY KEY
. SQL Server 2014 представил встроенный синтаксис индекса для подмножества параметров, доступных вCREATE INDEX
. С тех пор это было расширено, чтобы позволить фильтровать условия индекса. Однако по-прежнему невозможно создать индексы с столбцамиINCLUDE
-d или индексами columnstore для переменных таблицы. -
Если вы будете многократно добавлять и удалять большое количество строк в таблице, используйте таблицу
#temporary
. Это поддерживаетTRUNCATE
(который более эффективен, чемDELETE
для больших таблиц), и дополнительно последующие вставки послеTRUNCATE
могут иметь лучшую производительность, чем те, которые следуют заDELETE
как показано здесь. - Если вы будете удалять или обновлять большое количество строк, тогда временная таблица может работать намного лучше, чем переменная таблицы - если она может использовать совместное использование набора строк (например, см. "Эффекты совместного использования набора строк" ниже).
- Если оптимальный план с использованием таблицы будет варьироваться в зависимости от данных, используйте таблицу
#temporary
. Это поддерживает создание статистики, которая позволяет динамически перекомпилировать план в соответствии с данными (хотя для кэшированных временных таблиц в хранимых процедурах поведение перекомпиляции необходимо понимать отдельно). - Если оптимальный план для запроса с использованием таблицы вряд ли когда-либо изменится, вы можете рассмотреть возможность использования табличной переменной, чтобы пропустить издержки, связанные с созданием статистики и перекомпиляцией (возможно, потребуются подсказки, чтобы исправить план, который вы хотите).
- Если источником данных, вставляемых в таблицу, является потенциально дорогой
SELECT
то учтите, что использование табличной переменной заблокирует возможность этого при использовании параллельного плана. - Если вам нужны данные в таблице, чтобы выдержать откат внешней пользовательской транзакции, используйте переменную таблицы. Возможным вариантом использования для этого может быть регистрация хода выполнения различных шагов в длинном пакете SQL.
- При использовании таблицы
#temp
в пользовательской транзакции блокировки могут удерживаться дольше, чем для табличных переменных (возможно, до конца транзакции или конца инструкции в зависимости от типа блокировки и уровня изоляции), а также это может предотвратить усечение транзакцииtempdb
войти, пока пользовательская транзакция не закончится. Так что это может способствовать использованию табличных переменных. - В хранимых подпрограммах можно кэшировать как переменные таблицы, так и временные таблицы. Содержание метаданных для кэшированных табличных переменных меньше, чем для
#temporary
таблиц. Боб Уорд указывает в своей презентации на базе данныхtempdb
что это может вызвать дополнительную конкуренцию системным таблицам в условиях высокого параллелизма. Кроме того, при работе с небольшими объемами данных это может существенно повлиять на производительность.
Эффекты совместного использования набора строк
DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);
CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);
INSERT INTO @T
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2
SET STATISTICS TIME ON
/*CPU time = 7016 ms, elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;
/*CPU time = 6234 ms, elapsed time = 7236 ms.*/
DELETE FROM @T
/* CPU time = 828 ms, elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;
/*CPU time = 672 ms, elapsed time = 980 ms.*/
DELETE FROM #T
DROP TABLE #T