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

298

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

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

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

yman
источник
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