Когда я должен использовать переменную таблицы против временной таблицы на сервере SQL?


298

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

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

Но я не знаю, что такое «слишком много записей». 100 000 записей? или 1000 000 записей? Как я могу узнать, находится ли используемая в таблице переменная в памяти или на диске? Есть ли какая-либо функция или инструмент в SQL Server 2005, чтобы измерить масштаб табличной переменной или сообщить мне, когда табличная переменная помещается на диск из памяти?


5
Табличная переменная почти всегда находится внутри tempDB- это «в памяти» - это миф. Также: переменные таблицы всегда будут считаться оптимизатором запросов для хранения ровно одной строки - если у вас их намного больше, это может привести к серьезным неудачным планам выполнения.
— marc_s

Вы можете найти это полезным stackoverflow.com/questions/27894/…
— Игорь Борисенко

2
@marc_s - Вы можете отбросить «почти» в этом утверждении. Это всегда в tempdb(но может быть и полностью в памяти)
— Мартин Смит

2
С SQL 2014 вы можете теперь создать табличную переменную в памяти
— папараццо

Ответы:


362

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

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

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

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

  1. Если вам нужен индекс, который нельзя создать для табличной переменной, тогда вам, конечно, понадобится #temporaryтаблица. Детали этого зависят от версии, однако. Для SQL Server 2012 и ниже единственными индексами, которые могли быть созданы для табличных переменных, были те, которые неявно создавались с помощью ограничения UNIQUEили PRIMARY KEY. SQL Server 2014 ввел синтаксис встроенного индекса для подмножества параметров, доступных в CREATE INDEX. С тех пор это было расширено, чтобы позволить фильтровать условия индекса. Однако индексы с INCLUDEстолбцами -d или индексами columnstore все еще невозможно создать для переменных таблицы.

  2. Если вы будете многократно добавлять и удалять большое количество строк в таблице, используйте #temporaryтаблицу. Это поддерживает TRUNCATE(что более эффективно, чем DELETEдля больших таблиц), и дополнительно последующие вставки после a TRUNCATEмогут иметь лучшую производительность, чем те, которые следуют за a, DELETE как показано здесь .

  3. Если вы будете удалять или обновлять большое количество строк, тогда временная таблица может работать намного лучше, чем переменная таблицы - если она может использовать совместное использование набора строк (например, см. «Эффекты совместного использования набора строк» ​​ниже).
  4. Если оптимальный план с использованием таблицы будет зависеть от данных, используйте #temporaryтаблицу. Это поддерживает создание статистики, которая позволяет динамически перекомпилировать план в соответствии с данными (хотя для кэшированных временных таблиц в хранимых процедурах поведение перекомпиляции необходимо понимать отдельно).
  5. Если оптимальный план для запроса, использующего таблицу, вряд ли когда-либо изменится, вы можете рассмотреть возможность использования табличной переменной, чтобы пропустить накладные расходы на создание и перекомпиляцию статистики (возможно, потребуются подсказки, чтобы исправить план, который вы хотите).
  6. Если источником данных, вставленных в таблицу, являются потенциально дорогие SELECT оператор, то учтите, что использование табличной переменной заблокирует возможность этого при использовании параллельного плана.
  7. Если вам нужны данные в таблице, чтобы пережить откат внешней пользовательской транзакции, используйте переменную таблицы. Возможным вариантом использования этого может быть регистрация хода выполнения различных шагов в длинном пакете SQL.
  8. При использовании #tempтаблицы внутри пользовательской блокировки транзакции могут удерживаться дольше, чем для табличных переменных (возможно, до конца транзакции или конца оператора в зависимости от типа блокировки и уровня изоляции), а также это может предотвратить усечение журнала tempdbтранзакций до тех пор, пока Пользовательская транзакция заканчивается. Так что это может способствовать использованию табличных переменных.
  9. В хранимых подпрограммах можно кэшировать как переменные таблицы, так и временные таблицы. Содержание метаданных для кэшированных табличных переменных меньше, чем для #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

2
Привет, мистер Мартин Смит. В моем случае я просто хочу сохранить набор значений Ids, чтобы использовать их в запросе других внутри процедуры Store. Так что вы мне порекомендуете?
— Джанкарло Фонтальво

@JeancarloFontalvo - переменная таблицы с включенным первичным ключом idи ее использованием OPTION (RECOMPILE), вероятно, подойдет для этого - но протестируйте оба.
— Мартин Смит,

состязание метаданных одинаково для временной таблицы и табличной переменной?
— Сайед Акил Ашик

@Syed. Как правило, меньше для телевидения. Блокировки могут быть сняты ранее, если внутри пользовательской транзакции. Также см. Ссылку Боба Уорда.
— Мартин Смит

73

Используйте переменную таблицы if для очень небольшого количества данных (тысячи байтов)

Используйте временную таблицу для большого количества данных

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

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

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

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

Мое понимание в основном основано на http://www.developerfusion.com/article/84397/table-variables-v-teilitary-tables-in-sql-server/ , который имеет гораздо больше деталей.


Вывод: переменная таблицы подходит для небольшого набора данных, но используйте временную таблицу для большего набора данных. У меня есть запрос с тысячами строк. При переключении с табличной переменной на временную таблицу время запроса сокращается с 40 с до всего 5 с при прочих равных условиях.
— Лян

42

Microsoft говорит здесь

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


14

Я полностью согласен с Abacus (извините - не хватает очков, чтобы комментировать).

Кроме того, имейте в виду, что это не обязательно сводится к тому, сколько записей у вас есть, но к размеру ваших записей.

Например, рассматривали ли вы разницу в производительности между 1000 записей с 50 столбцами каждая против 100 000 записей только с 5 столбцами каждая?

Наконец, может быть, вы запрашиваете / храните больше данных, чем вам нужно? Вот хорошее прочтение о стратегиях оптимизации SQL . Ограничьте объем данных, которые вы извлекаете, особенно если вы не используете их все (некоторые программисты SQL становятся ленивыми и просто выбирают все, даже если они используют только небольшое подмножество). Не забывайте, что анализатор SQL-запросов также может стать вашим лучшим другом.


4

Таблица переменных доступна только для текущего сеанса, например, если вам нужна EXECдругая хранимая процедура в текущем сеансе, вам придется пропустить таблицу, так как, Table Valued Parameterконечно, это повлияет на производительность, с временными таблицами вы можете сделать это только с передача имени временной таблицы

Чтобы проверить временную таблицу:

  • Редактор запросов Open Management Studio
  • Создать временную таблицу
  • Откройте другое окно редактора запросов
  • Выберите из этой таблицы "Доступно"

Чтобы проверить таблицу переменных:

  • Редактор запросов Open Management Studio
  • Создать таблицу переменных
  • Откройте другое окно редактора запросов
  • Выберите из этой таблицы «Недоступно»

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


3

записывая данные в объявленные таблицы declare @tbи после объединения с другими таблицами, я понял, что время отклика по сравнению с временными таблицами tempdb .. # tbнамного выше.

Когда я присоединяюсь к ним с помощью @tb, время для возврата результата намного больше, в отличие от #tm , возвращение происходит практически мгновенно.

Я выполнил тесты с 10 000 строк, соединяющих и соединяющих с 5 другими таблицами


Не могли бы вы опубликовать тест, который вы провели, чтобы получить эти цифры?
— Dan Def
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.