В чем разница между CTE и Temp Table?

174

В чем разница между общим выражением таблицы (CTE) и временной таблицей? И когда я должен использовать один над другим?

КТР

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

Temp Table

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
Рейчел
источник

Ответы:

200

Это довольно широкий вопрос, но я дам вам общий ответ, как только смогу.

, CTE ...

  • Не подлежат индексации (но могут использовать существующие индексы для объектов, на которые есть ссылки)
  • Не может иметь ограничений
  • А по существу , одноразовые VIEWs
  • Сохранять только до следующего запроса
  • Может быть рекурсивным
  • Не иметь специальной статистики (полагаться на статистику базовых объектов)

#Temp Tables ...

  • Реальные материализованные таблицы, которые существуют в базе данных tempdb
  • Может быть проиндексирован
  • Может иметь ограничения
  • Упорствовать на всю жизнь текущего СОЕДИНЕНИЯ
  • Может ссылаться на другие запросы или подпроцедуры
  • Выделенная статистика генерируется двигателем

Что касается того, когда использовать каждый, у них очень разные варианты использования. Если у вас будет очень большой набор результатов или вам нужно будет ссылаться на него более одного раза, поместите его в #tempтаблицу. Если он должен быть рекурсивным, одноразовым или просто для упрощения чего-либо логически, CTEпредпочтительным является a .

Кроме того , CTEне должно никогда быть использовано для выполнения . Вы почти никогда не будете ускорять процесс с помощью CTE, потому что, опять же, это просто одноразовое представление. Вы можете делать с ними некоторые полезные вещи, но ускорение запроса на самом деле не является одним из них.

JNK
источник
ускорение большого MERGE с использованием CTE - это вещь
AgentFire
1
Ускорение многих запросов с использованием CTE - это тоже вещь, потому что с CTE вы можете добавить свои собственные знания бизнеса, чтобы опередить оптимизатор запросов. Например, вы можете выбрать часть 1 вашего CTE из таблиц, где вы знаете, что результирующие строки будут очень маленькими. Внутри того же запроса вы можете присоединить этот крошечный набор результатов к какому-либо большему набору результатов и полностью обойти проблемы, вызванные устаревшей статистикой и т. Д. Чтобы сделать это, вам нужно добавить подсказки запроса, чтобы форсировать порядок. Это работает, это улучшает производительность.
Дейв Хильдич
«никогда не использовать для производительности» - это широкое и несколько субъективное утверждение, хотя я понимаю вашу точку зрения. Хотя, в дополнение к другим комментариям, возможен еще один потенциальный выигрыш в производительности при использовании CTE при переключении на рекурсивный CTE из другой формы рекурсии, такой как рекурсивные вызовы процедур или курсор.
JD
29

РЕДАКТИРОВАТЬ:

Пожалуйста, смотрите комментарии Мартина ниже:

CTE не материализуется как таблица в памяти. Это просто способ инкапсуляции определения запроса. В случае с OP он будет встроен и так же, как просто делает SELECT Column1, Column2, Column3 FROM SomeTable. Большую часть времени они не материализуются заранее, поэтому они не возвращают строк WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, а также проверяют планы выполнения. Хотя иногда можно взломать план, чтобы получить катушку. Есть пункт подключения, запрашивающий подсказку для этого. - Мартин Смит 15 февраля '12 в 17:08


Оригинальный ответ

КТР

Узнайте больше на MSDN

CTE создает таблицу, используемую в памяти, но она действительна только для конкретного запроса, следующего за ним. При использовании рекурсии это может быть эффективной структурой.

Вы также можете рассмотреть возможность использования табличной переменной. Это используется как временная таблица и может использоваться несколько раз без необходимости повторной материализации для каждого соединения. Кроме того, если вам нужно сохранить несколько записей сейчас, добавить еще несколько записей после следующего выбора, добавить еще несколько записей после другой операции, а затем вернуть только те несколько записей, тогда это может быть удобной структурой, поскольку не нужно бросать после казни. В основном просто синтаксический сахар. Однако, если вы сохраняете низкое количество строк, оно никогда не материализуется на диск. См. В чем разница между временной таблицей и табличной переменной в SQL Server? Больше подробностей.

Temp Table

Узнайте больше на MSDN - прокрутите около 40% пути

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

Временные таблицы бывают двух видов: локальные и глобальные. В терминах MS Sql Server вы используете #tableNameобозначение для локального и ##tableNameобозначение для глобального (обратите внимание на использование одинарного или двойного # в качестве идентифицирующей характеристики).

Обратите внимание, что с временными таблицами, в отличие от табличных переменных или CTE, вы можете применять индексы и т. П., Поскольку они являются законными таблицами в обычном смысле этого слова.


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

Jcolebrand
источник
11
CTE не материализуется как таблица в памяти. Это просто способ инкапсуляции определения запроса. В случае с ОП это будет встроено и точно так же, как простоSELECT Column1, Column2, Column3 FROM SomeTable
Мартин Смит
4
Большую часть времени они не материализуются заранее, поэтому они не возвращают строк WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, а также проверяют планы выполнения. Хотя иногда можно взломать план, чтобы получить катушку. Есть пункт подключения, запрашивающий подсказку для этого.
Мартин Смит
16

Принял ответ здесь говорит «КТР никогда не должен быть использован для выполнения» , - но это может ввести в заблуждение. В контексте CTE по сравнению с временными таблицами, я только что закончил удаление полосы мусора из набора хранимых процедур, потому что некоторые глупцы, должно быть, думали, что использование временных таблиц было незначительным или отсутствовало. Я вложил все это в CTE, за исключением тех, которые на законных основаниях собирались повторно использовать на протяжении всего процесса. Я получил около 20% производительности по всем показателям. Затем я приступил к удалению всех курсоров, которые пытались реализовать рекурсивную обработку. Именно здесь я увидел наибольшую выгоду. В итоге я сократил время отклика в десять раз.

CTE и временные таблицы имеют очень разные варианты использования. Я просто хочу подчеркнуть, что, хотя это и не панацея, понимание и правильное использование CTE могут привести к некоторым по-настоящему звездным улучшениям как качества / удобства сопровождения, так и скорости. Так как я справился с ними, я вижу временные таблицы и курсоры как большое зло обработки SQL. Теперь я могу прекрасно справляться с табличными переменными и CTE практически для всего. Мой код чище и быстрее.

Мел Падден
источник
Теперь давайте будем честными - курсоры - это великое зло; временные таблицы - в худшем случае меньшее зло. :-) Это действительно несправедливо , чтобы положить их на том же уровне, как вы видели себя.
RDFozz
@RDFozz верно, у ада 9 кругов, как мы все знаем . Давайте установим временные таблицы на 2-й, а курсоры на ... 7-й? ;)
ypercubeᵀᴹ
1
Вы знаете, что такое «великое зло» в программировании? Когда люди говорят, что определенная техника - это зло. Есть место для курсоров. Они могут превзойти другие методы в определенных сценариях. Здесь нет зла - вам нужно научиться использовать правильный инструмент для работы. Измеряйте то, что вы делаете, и не верьте обману того, что CTE, Temp Tables или Cursors - это зло. Мера - потому что правда зависит от сценария.
Дейв Хильдич
@DaveHilditch - это справедливый комментарий, но это также справедливый комментарий, чтобы утверждать, что в очень, очень многих ситуациях курсоры не являются правильным решением, так что это практичное обобщение, чтобы иметь их, а почти в крайнем случае.
Мел Падден
1
По моему опыту, КУРСОР сам по себе неплох. КУРСОРЫ обычно «ошибочно» используются разработчиками, потому что в большинстве языков программирования вы должны мыслить итеративно, в отличие от SQL, где вы в основном должны мыслить пакетно. Я знаю, что это обычная ошибка на моем рабочем месте, когда разработчики просто не могут «увидеть» выход из проблемы, кроме как с КУРСОРОМ, поэтому хороший БД пригодится для их обучения и исправления. @DaveHilditch совершенно прав: правильный инструмент для правильной работы - это все, что нужно.
Филипп
14

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

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

Переменные таблицы IIRC (с другой стороны) всегда являются структурами в памяти.

ConcernedOfTunbridgeWells
источник
4
CTE могут быть параметризованы? Как? Кроме того, переменные таблицы не всегда являются структурами в памяти. Посмотрите превосходный ответ Мартина на связанный вопрос.
Пол Уайт
11

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

Олег Док
источник
8

Основной причиной использования CTE является доступ к функциям окна, таким как row_number()и другие.

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

with reallyfastcte as (
select *, 
row_number() over (partition by groupingcolumn order by sortingcolumn) as rownum
from sometable
)
select *
from reallyfastcte
where rownum = 1;

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

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

Кроме того, CTE могут повысить производительность, если вы понимаете свою бизнес-логику и знаете, какие части запроса должны выполняться первыми - как правило, сначала ставьте самые избирательные запросы, которые приводят к наборам результатов, которые могут использовать индекс при следующем соединении, и добавляйте option(force order)запрос. намек

Наконец, CTE не используют базу данных tempdb по умолчанию, поэтому вы можете уменьшить количество конфликтов в этом узком месте за счет их использования.

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

Дейв Хилдич
источник
все хорошие очки ... +1
Мел Падден
6

Кажется, здесь есть немного негатива по отношению к CTE.

Мое понимание CTE - то, что это в основном вид ad-hoc. SQL является декларативным и основанным на множестве языком. CTE - отличный способ объявить сет! Неспособность индексировать CTE - это действительно хорошая вещь, потому что вам это не нужно! Это действительно своего рода синтаксический сахар, облегчающий чтение / запись запроса. Любой приличный оптимизатор разработает лучший план доступа, используя индексы на базовых таблицах. Это означает, что вы можете эффективно ускорить ваш запрос CTE, следуя советам по индексам для базовых таблиц.

Кроме того, то, что вы определили набор как CTE, не означает, что все строки в наборе должны быть обработаны. В зависимости от запроса оптимизатор может обработать «достаточно» строк, чтобы удовлетворить запрос. Может быть, вам нужны только первые 20 или около того для вашего экрана. Если вы построили временную таблицу, то вам действительно нужно читать / записывать все эти строки!

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

Я должен признать, что мой опыт в основном связан с DB2, поэтому я предполагаю, что CTE работает одинаково в обоих продуктах. Я с радостью исправлюсь, если CTE каким-то образом уступает SQL-серверу. ;)

Бен Терли
источник