Это, кажется, область с довольно многими мифами и противоречивыми взглядами.
Так в чем же разница между табличной переменной и локальной временной таблицей в SQL Server?
sql-server
t-sql
temporary-tables
Мартин Смит
источник
источник
Ответы:
содержание
Предостережение
В этом ответе рассматриваются «классические» табличные переменные, представленные в SQL Server 2000. SQL Server 2014 в памяти OLTP представляет оптимизированные для памяти типы таблиц. Экземпляры табличных переменных во многом отличаются от тех, которые обсуждались ниже! ( подробнее ).
Место хранения
Нет разницы. Оба хранятся в
tempdb
.Я видел, что это предположило, что для табличных переменных это не всегда так, но это можно проверить из приведенного ниже
Пример результатов (показаны местоположения в
tempdb
двух строках)Логическое Расположение
@table_variables
ведут себя больше, как если бы они были частью текущей базы данных, чем#temp
таблицы. Для табличных переменных (с 2005 года) параметры сортировки столбцов, если они не указаны явно, будут сопоставлениями текущей базы данных, тогда как для#temp
таблиц будут использоваться параметры сортировки по умолчаниюtempdb
( Подробнее ). Кроме того, определяемые пользователем типы данных и коллекции XML должны быть в#temp
базе данных tempdb для использования в таблицах, но переменные таблицы могут использовать их из текущей базы данных ( Source ).SQL Server 2012 представляет автономные базы данных. Поведение временных таблиц у этих отличается (ч / т Аарон)
Видимость в разных сферах
@table_variables
доступен только в пределах пакета и области, в которой они объявлены.#temp_tables
доступны в дочерних пакетах (вложенные триггеры, процедуры,exec
вызовы).#temp_tables
созданный во внешней области видимости (@@NESTLEVEL=0
) может также охватывать пакеты, поскольку они сохраняются до завершения сеанса. Ни один тип объекта не может быть создан в дочернем пакете и доступен в области вызова, как обсуждено далее ( хотя глобальные##temp
таблицы могут быть).Продолжительность жизни
@table_variables
создаются неявно, когда выполняется пакет, содержащийDECLARE @.. TABLE
оператор (до запуска любого пользовательского кода в этом пакете), и неявно удаляются в конце.Хотя синтаксический анализатор не позволит вам попробовать и использовать переменную таблицы перед
DECLARE
оператором, неявное создание можно увидеть ниже.#temp_tables
создаются явно, когдаCREATE TABLE
встречается оператор TSQL, и могут быть удалены явноDROP TABLE
или будут удалены неявно, когда пакет заканчивается (если он создан в дочернем пакете с@@NESTLEVEL > 0
) или когда сеанс заканчивается иначе.NB. Внутри хранимых процедур можно кэшировать оба типа объектов , а не многократно создавать и удалять новые таблицы. Существуют ограничения на то, когда может происходить это кэширование, которые можно нарушать,
#temp_tables
но которые в@table_variables
любом случае предотвращают ограничения . Затраты на обслуживание для кэшированных#temp
таблиц немного больше, чем для табличных переменных, как показано здесь .Метаданные объекта
Это по сути одинаково для обоих типов объектов. Хранится в системных базовых таблицах в
tempdb
. Однако более просто увидеть#temp
таблицу, так какOBJECT_ID('tempdb..#T')
ее можно использовать для ввода в системные таблицы, и внутренне сгенерированное имя более тесно связано с именем, определенным вCREATE TABLE
операторе. Для табличных переменныхobject_id
функция не работает, и внутреннее имя полностью сгенерировано системой без связи с именем переменной. Ниже показано, что метаданные все еще там, однако вводя (надеюсь уникальное) имя столбца. Для таблиц без уникальных имен столбцов object_id может быть определен с использованиемDBCC PAGE
тех пор, пока они не пусты.Выход
операции
Операции над
@table_variables
выполняются как системные транзакции, независимые от какой-либо внешней пользовательской транзакции, тогда как эквивалентные#temp
табличные операции будут выполняться как часть самой пользовательской транзакции. По этой причинеROLLBACK
команда повлияет на#temp
таблицу, но оставит@table_variable
нетронутым.логирование
Оба генерируют записи журнала в
tempdb
журнал транзакций. Распространенным заблуждением является то, что это не относится к переменным таблицы, поэтому сценарий, демонстрирующий это ниже, объявляет переменную таблицы, добавляет пару строк, затем обновляет их и удаляет их.Поскольку переменная таблицы создается и неявно отбрасывается в начале и конце пакета, необходимо использовать несколько пакетов, чтобы увидеть полное ведение журнала.
Возвращает
Детальный просмотр
Сводное представление (включает ведение журнала для неявного удаления и системные базовые таблицы)
Насколько я мог различить операции на обоих генерируют примерно одинаковое количество журналирования.
Хотя количество журналов очень схоже, одно важное отличие состоит в том, что записи журналов, относящиеся к
#temp
таблицам, не могут быть очищены до тех пор, пока какая-либо содержащая пользовательская транзакция не завершит настолько длительную транзакцию, что в какой-то момент запись в#temp
таблицы предотвратит усечение журнала,tempdb
тогда как автономные транзакции порожденные для табличных переменных нет.Переменные таблицы не поддерживают,
TRUNCATE
поэтому могут быть в невыгодном положении при ведении журнала, когда требуется удалить все строки из таблицы (хотя для очень маленьких таблицDELETE
может работать лучше в любом случае )мощность
Многие планы выполнения, включающие в себя переменные таблицы, будут показывать одну строку, оцениваемую как выходные данные из них. Проверка свойств табличной переменной показывает, что SQL Server считает, что переменная таблицы имеет нулевые строки (почему она оценивает, что 1 строка будет выведена из таблицы с нулевой строкой, объясняется здесь @Paul White ).
Однако результаты, показанные в предыдущем разделе, показывают точное
rows
количество вsys.partitions
. Проблема в том, что в большинстве случаев операторы, ссылающиеся на переменные таблицы, компилируются, пока таблица пуста. Если оператор (пере) скомпилирован после@table_variable
заполнения, то вместо этого он будет использоваться для количества элементов таблицы (это может произойти из-за явногоrecompile
или, возможно, потому что оператор также ссылается на другой объект, который вызывает отложенную компиляцию или перекомпиляцию.)План показывает точное количество строк после отложенной компиляции.
В SQL Server 2012 SP2 введен флаг трассировки 2453. Более подробная информация находится в разделе «Реляционный двигатель» здесь .
Когда этот флаг трассировки активирован, это может привести к тому, что автоматическая перекомпиляция будет учитывать измененную мощность, как будет обсуждаться ниже.
Примечание: в Azure на уровне совместимости 150 компиляция оператора теперь откладывается до первого выполнения . Это означает, что он больше не будет подвержен проблеме оценки нулевой строки.
Нет столбцов статистики
Наличие более точного количества элементов в таблице не означает, что расчетное количество строк будет более точным (если только не выполнить операцию со всеми строками в таблице). SQL Server вообще не поддерживает статистику столбцов для табличных переменных, поэтому будет использовать догадки, основанные на предикате сравнения (например, 10% таблицы будет возвращено для
=
неуникального столбца или 30% для>
сравнения). В отличие от статистики столбцов будут поддерживаться для#temp
таблиц.SQL Server ведет подсчет количества изменений, внесенных в каждый столбец. Если количество изменений с момента составления плана превышает порог перекомпиляции (RT), то план будет перекомпилирован и статистика обновлена. RT зависит от типа и размера стола.
Из плана кэширования в SQL Server 2008
KEEP PLAN
намек может быть использован для установки RT для#temp
таблиц таких же , как для постоянных таблиц.Общий эффект всего этого заключается в том, что часто планы выполнения, сгенерированные для
#temp
таблиц, на несколько порядков лучше, чем@table_variables
когда задействовано много строк, так как SQL Server имеет лучшую информацию для работы.NB1: переменные таблицы не имеют статистики, но могут по-прежнему вызывать событие перекомпиляции «Статистика изменена» под флагом трассировки 2453 (не применяется к «тривиальным» планам). Это происходит при тех же пороговых значениях перекомпиляции, как показано для временных таблиц выше с дополнительный, если
N=0 -> RT = 1
. то есть все операторы, скомпилированные, когда переменная таблицы пуста, в итоге получат перекомпиляцию и исправятсяTableCardinality
при первом выполнении, когда они не пусты. Количество элементов таблицы времени компиляции сохраняется в плане, и если оператор выполняется снова с тем же количеством элементов (либо из-за потока управляющих операторов, либо из-за повторного использования кэшированного плана), перекомпиляция не происходит.NB2. Для кэшированных временных таблиц в хранимых процедурах история перекомпиляции гораздо сложнее, чем описано выше. См. Временные таблицы в хранимых процедурах для всех кровавых деталей.
перекомпилирует
Кроме описанных выше
#temp
таблиц на основе модификаций, таблицы также могут быть связаны с дополнительными компиляциями просто потому, что они разрешают операции, которые запрещены для табличных переменных, запускающих компиляцию (например, изменения DDLCREATE INDEX
,ALTER TABLE
)Блокировка
Было заявлено, что табличные переменные не участвуют в блокировке. Это не вариант. Выполнение приведенных ниже выводов на вкладку сообщений SSMS с подробностями блокировок, снятых и снятых для оператора вставки.
Для запросов, которые
SELECT
из табличных переменных Пол Уайт указывает в комментариях, что они автоматически приходят с неявнымNOLOCK
намеком. Это показано нижеВыход
Однако влияние этого на блокировку может быть незначительным.
Ни один из этих возвращаемых результатов не приводит к порядку ключа индекса, указывающему, что SQL Server использовал упорядоченное сканирование для обоих.
Я запустил вышеупомянутый скрипт дважды, и результаты для второго запуска ниже
Вывод блокировки для табличной переменной действительно крайне минимален, поскольку SQL Server просто получает блокировку стабильности схемы для объекта. Но для
#temp
стола это почти так же легко, как дляS
блокировки уровня объекта .NOLOCK
Намек илиREAD UNCOMMITTED
изоляция уровень , конечно , может быть задан в явном виде при работе с#temp
таблицами , а также.Подобно проблеме с регистрацией транзакции окружающего пользователя, это может означать, что блокировки для
#temp
таблиц дольше удерживаются . Сценарий нижепри запуске вне явной пользовательской транзакции в обоих случаях единственной блокировкой, возвращаемой при проверке,
sys.dm_tran_locks
является общая блокировка дляDATABASE
.При раскомментировании
BEGIN TRAN ... ROLLBACK
возвращается 26 строк, показывающих, что блокировки удерживаются как на самом объекте, так и на строках системной таблицы, чтобы обеспечить откат и предотвратить чтение незафиксированных данных другими транзакциями. Операция с эквивалентной табличной переменной не подлежит откату с пользовательской транзакцией и не нуждается в удержании этих блокировок, чтобы мы могли проверить следующую инструкцию, но отслеживание блокировок, полученных и снятых в Profiler, или использование флага трассировки 1200 показывает, что множество событий блокировки все еще работают происходят.Индексы
Для версий, предшествующих SQL Server 2014, индексы могут создаваться неявно только для табличных переменных как побочный эффект добавления уникального ограничения или первичного ключа. Это, конечно, означает, что поддерживаются только уникальные индексы. Однако неуникальный некластеризованный индекс в таблице с уникальным кластеризованным индексом можно смоделировать, просто объявив его
UNIQUE NONCLUSTERED
и добавив ключ CI в конец требуемого ключа NCI (SQL Server сделает это за кулисами в любом случае, даже если неуникальный NCI можно указать)Как было показано ранее, различные
index_option
s могут быть указаны в объявлении ограничения, включаяDATA_COMPRESSION
,IGNORE_DUP_KEY
иFILLFACTOR
(хотя нет смысла устанавливать его, поскольку это будет иметь какое-то значение только при перестроении индекса, и вы не можете перестроить индексы по табличным переменным!)Кроме того, переменные таблиц не поддерживают
INCLUDE
d столбцов, отфильтрованных индексов (до 2016 года) или секционирования, а#temp
таблицы - в (должна быть создана схема секционированияtempdb
).Индексы в SQL Server 2014
Неуникальные индексы могут быть объявлены встроенными в определении табличной переменной в SQL Server 2014. Пример синтаксиса для этого приведен ниже.
Индексы в SQL Server 2016
Из CTP 3.1 теперь можно объявлять отфильтрованные индексы для табличных переменных. В RTM может быть так, что включенные столбцы также разрешены, хотя они , скорее всего, не попадут в SQL16 из-за ограниченности ресурсов
параллелизм
Запросы, которые вставляются в (или иным образом изменяются),
@table_variables
не могут иметь параллельный план,#temp_tables
таким образом не ограничиваются.Существует очевидный обходной путь, заключающийся в том, что переписывание позволяет выполнить
SELECT
часть параллельно, но в итоге используется скрытая временная таблица (за кадром)Нет такого ограничения в запросах, которые выбирают из табличных переменных, как показано в моем ответе здесь
Другие функциональные различия
#temp_tables
не может использоваться внутри функции.@table_variables
может использоваться внутри UDFs скалярной таблицы или таблицы с несколькими утверждениями.@table_variables
не может иметь именованных ограничений.@table_variables
не может бытьSELECT
-edINTO
,ALTER
-ed,TRUNCATE
d или быть цельюDBCC
команд, таких какDBCC CHECKIDENT
или из,SET IDENTITY INSERT
и не поддерживает табличные подсказки, такие какWITH (FORCESCAN)
CHECK
ограничения на табличные переменные не рассматриваются оптимизатором для упрощения, подразумеваемых предикатов или обнаружения противоречий.PAGELATCH_EX
ожиданиями. ( Пример )Только память?
Как указано в начале, оба хранятся на страницах в
tempdb
. Однако я не говорил, было ли какое-либо различие в поведении, когда дело доходит до записи этих страниц на диск.Я провел небольшое тестирование по этому вопросу сейчас и до сих пор не видел такой разницы. В конкретном тесте, который я провел на своем экземпляре SQL Server, 250 страниц, по-видимому, являются точкой отсечения до того, как файл данных будет записан.
Запуск приведенного ниже скрипта
А мониторинг записи в
tempdb
файл данных с помощью Process Monitor я не видел ни одной (кроме случайных записей на странице загрузки базы данных со смещением 73 728). После перехода250
на251
я начал видеть записи, как показано ниже.На приведенном выше снимке экрана показаны записи 5 * 32 страниц и одна запись одной страницы, указывающая, что 161 страница была записана на диск. Я получил ту же точку отсечения 250 страниц при тестировании с табличными переменными тоже. Сценарий ниже показывает это по-другому, глядя на
sys.dm_os_buffer_descriptors
Результаты
Показывает, что 192 страницы были записаны на диск и грязный флаг очищен. Это также показывает, что запись на диск не означает, что страницы будут немедленно удалены из пула буферов. Запросы к этой табличной переменной все еще могут выполняться полностью из памяти.
На простаивающем сервере с
max server memory
установленным значением2000 MB
иDBCC MEMORYSTATUS
сообщением о страницах буферного пула, выделенных примерно в 1 843 000 КБ (около 23 000 страниц), я вставил их в таблицы выше партиями по 1000 строк / страниц и для каждой записанной итерации.И табличная переменная, и
#temp
таблица дали почти идентичные графики и сумели в значительной степени максимизировать пул буферов, прежде чем дошли до того, что они не были полностью сохранены в памяти, поэтому не существует каких-либо особых ограничений на объем памяти. любой может потреблять.источник
Есть несколько вещей, на которые я хотел бы обратить внимание, основываясь больше на конкретном опыте, чем на учебе. Как администратор базы данных, я очень новичок, поэтому, пожалуйста, исправьте меня там, где это необходимо.
источник