Какие более производительные, CTE или временные таблицы?

Какие из них более эффективны, CTE или Temporary Tables?


person Blankman    schedule 27.03.2009    source источник
comment
Связанный вопрос: dba.stackexchange.com/q/13112   -  person Rachel    schedule 22.01.2015
comment
Пользователи могут найти справочную информацию (не относящуюся к производительности) в Использование общих табличных выражений на сайте technet.microsoft.com.   -  person Sheridan    schedule 08.10.2015


Ответы (12)


Я бы сказал, что это разные концепции, но не слишком разные, чтобы сказать «мел и сыр».

  • Временная таблица хороша для повторного использования или для выполнения нескольких проходов обработки набора данных.

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

  • Временная таблица - это еще одна таблица с некоторыми правилами в отношении области видимости.

Я сохранил процессы, в которых я использую оба (и переменные таблицы тоже)

person gbn    schedule 30.03.2009
comment
Временные таблицы также позволяют использовать индексы и даже статистику, которые иногда необходимы, а CTE - нет. - person CodeCowboyOrg; 04.09.2014
comment
Я думаю, что этот ответ недостаточно подчеркивает тот факт, что CTE может привести к ужасной производительности. Обычно я обращаюсь к этому ответу на dba.stackexchange. Ваш вопрос занимает второе место в моей поисковой системе, если я ищу cte vs temporary tables, поэтому, IMHO, этот ответ должен лучше выделить недостатки CTE. TL; DR связанного ответа: CTE никогда не должен использоваться для производительности.. Я согласен с этой цитатой, так как испытал на себе недостатки CTE. - person TT.; 03.02.2016
comment
@TT. Интересный. Я считаю, что CTE работают намного лучше - person J.S. Orris; 21.07.2017

По-разному.

Прежде всего

Что такое обычное табличное выражение?

(Нерекурсивный) CTE обрабатывается очень аналогично другим конструкциям, которые также могут использоваться в качестве встроенных табличных выражений в SQL Server. Производные таблицы, представления и встроенные табличные функции. Обратите внимание, что хотя в BOL сказано, что CTE можно рассматривать как временный набор результатов, это чисто логическое описание. Чаще всего он не материализован сам по себе.

Что такое временная таблица?

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

Тестовые данные

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Пример 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

План 1

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

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Перезапись путем материализации CTE в промежуточную временную таблицу здесь была бы в значительной степени контрпродуктивной.

Материализация определения CTE

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Потребуется копирование около 8 ГБ данных во временную таблицу, тогда еще есть накладные расходы на выбор из нее.

Пример 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

Приведенный выше пример на моей машине занимает около 4 минут.

Только 15 строк из 1000000 случайно сгенерированных значений соответствуют предикату, но для их обнаружения требуется 16 раз дорогостоящее сканирование таблицы.

введите описание изображения здесь

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

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

С планом

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

В некоторых случаях SQL Server будет использовать спул для кэширования промежуточного результата, например CTE и избегайте повторной оценки этого поддерева. Это обсуждается в (перенесенном) элементе Connect Предоставьте подсказку для принудительной промежуточной материализации CTE или производных таблиц. Однако статистика по этому поводу не создается, и даже если количество буферизованных строк должно сильно отличаться от расчетного, план выполнения не может динамически адаптироваться в ответ (по крайней мере, в текущих версиях. Адаптивные планы запросов могут стать возможными в будущее).

person Martin Smith    schedule 05.10.2014
comment
Это единственный ответ, который отвечает на фактический вопрос (который спрашивает, какая производительность лучше, а не какая разница или какая ваша любимая), и он отвечает на этот вопрос правильно: это зависит от того, какой ответ правильный. Это также единственный ответ с подтверждающими данными для объяснения, несколько других (с большим количеством голосов) делают определенные утверждения, что один лучше, чем другой, без каких-либо ссылок или доказательств ... Чтобы было ясно, все эти ответы также < б> неверно. Потому что это зависит - person Arkaine55; 06.07.2015
comment
Это также хорошо написанный ответ с хорошими ссылками. Серьезно на высшем уровне. - person Dan Williams; 15.08.2019
comment
Мне нравится выделять эту часть, которая, как я считаю, верна. Промежуточная материализация части запроса во временную таблицу иногда может быть полезной, даже если она оценивается только один раз. - person Mark Monforti; 02.09.2020

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

person CSW    schedule 30.04.2010
comment
Я видел это на собственном опыте. CTE работают значительно медленнее. - person goku_da_master; 25.01.2011
comment
CTE также работают медленнее, потому что результаты не кэшируются. Поэтому каждый раз, когда вы используете CTE, он повторно запускает запрос, план и все остальное. - person goku_da_master; 25.01.2011
comment
И механизм db может выбрать повторный запуск запроса не только для каждой ссылки, но и для каждой строки потребительского запроса, как коррелированный подзапрос ... вы всегда должны следить за этим, если он не желательно. - person Mike M; 11.07.2014
comment
Таблица temp хранится в tempdb на SQL Server, который является диском, но имеет преимущество индексирования, и в этом случае оптимизатор SQL хорошо работает с запросами на выборку. Не уверен, в какой db или области диска хранится CTE (когда он превышает размер памяти и ставится в очередь для подкачки ввода-вывода), но он никогда не оптимизируется с большим объемом данных. Я иногда использовал параметр компилятора (с перекомпиляцией), чтобы ускорить его - person rmehra76; 16.06.2020

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

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

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

Итак, в итоге нет четкого ответа, но лично я предпочел бы CTE временным таблицам.

person marc_s    schedule 27.03.2009
comment
В случае SQLite и PostgreSQL временные таблицы автоматически удаляются (обычно в конце сеанса). А вот насчет других СУБД я не знаю. - person Serrano; 11.02.2013
comment
CTE похож на временное представление. Данные AFAIK не хранятся, поэтому ничего не хранится в памяти или на диске. Важное примечание: каждый раз, когда вы используете CTE, запрос запускается снова. - person Rob; 29.04.2013
comment
Лично я никогда не видел, чтобы CTE работал лучше, чем таблица Temp для скорости. И отладка с временной таблицей намного проще - person Mark Monforti; 14.09.2016

Итак, запрос, который мне поручили оптимизировать, был написан с двумя CTE на сервере SQL. Прошло 28 секунд.

Я потратил две минуты на преобразование их во временные таблицы, а запрос занял 3 секунды.

Я добавил индекс в временную таблицу в поле, к которому он был присоединен, и уменьшил его до 2 секунд.

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

Самое безумное, что CTE использовались только один раз, и индексирование их оказалось на 50% быстрее.

person Mark Monforti    schedule 14.09.2016

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

Например, я создал sprocs, которые возвращают результаты больших вычислений за 15 секунд, но конвертируют этот код для работы в CTE, и видел, как он работал более 8 минут для достижения тех же результатов.

person Andy_RC    schedule 01.09.2014
comment
Да, мне нравится этот комментарий. Кажется, существует такая странная парадигма, что если я могу написать что-то одной строкой кода вместо двух, я должен это сделать. Я сейчас отлаживаю что-то, в которое вложено 13 CTE, и CTE называются data1-data13. Полное безумие. - person Mark Monforti; 02.09.2020

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

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

Объем временной таблицы только в рамках сеанса. EX: открыть два окна SQL-запроса.

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Запустите этот запрос в первом окне, затем запустите приведенный ниже запрос во втором окне, вы можете найти разницу.

select * from #temp
person selvaraj    schedule 30.05.2011
comment
›› это просто набор результатов, который мы можем использовать для соединения. - ›Это неточно. CTE - это не набор результатов, а встроенный код. Механизм запросов SQL Server анализирует код CTE как часть текста запроса и строит в соответствии с ним план выполнения. Идея о том, что CTE является встроенным, является большим преимуществом использования CTE, поскольку он позволяет серверу создавать комбинированный план выполнения. - person Ronen Ariely; 29.08.2015

Поздно на вечеринку, но ...

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

IOW, я не могу обычно разрабатывать хранимую процедуру или UDF или временные таблицы и т. Д. Мне в значительной степени приходится делать все через интерфейс МОЕГО приложения (Crystal Reports - таблицы добавления / связывания, установка предложений where из ж / в ЧР и др.). Одна НЕБОЛЬШАЯ изюминка заключается в том, что Crystal позволяет мне использовать КОМАНДЫ (а также выражения SQL). Некоторые вещи, которые неэффективны при использовании обычных возможностей добавления / связывания таблиц, можно сделать, определив команду SQL. Я использую CTE через это и получил очень хорошие результаты "удаленно". CTE также помогают с обслуживанием отчетов, не требуя разработки кода, передачи его администратору баз данных для компиляции, шифрования, передачи, установки и последующего многоуровневого тестирования. Я могу выполнять CTE через локальный интерфейс.

Обратной стороной использования CTE с CR является то, что каждый отчет является отдельным. Каждый CTE должен поддерживаться для каждого отчета. Там, где я могу выполнять SP и UDF, я могу разработать что-то, что может использоваться в нескольких отчетах, требуя только ссылки на SP и передачи параметров, как если бы вы работали с обычной таблицей. CR не очень хорошо справляется с обработкой параметров в командах SQL, поэтому этот аспект аспекта CR / CTE может отсутствовать. В таких случаях я обычно пытаюсь определить CTE для возврата достаточного количества данных (но не ВСЕХ данных), а затем использую возможности выбора записей в CR, чтобы разрезать их и нарезать кубиками.

Итак ... я голосую за CTE (пока я не получу свое пространство данных).

person Marc    schedule 18.05.2011

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

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

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

person purchas    schedule 06.09.2012
comment
Кроме того, я думаю, что, поскольку я использую только CTE в соединении, я действительно выполняю CTE только один раз в моем запросе, поэтому кеширование результатов не было такой большой проблемой в этом отношении - person purchas; 06.09.2012

Это действительно открытый вопрос, и все зависит от того, как он используется, и от типа временной таблицы (переменная таблицы или традиционная таблица).

Традиционная временная таблица хранит данные во временной базе данных, что действительно замедляет работу временных таблиц; однако табличных переменных нет.

person JoshBerke    schedule 27.03.2009

Я только что протестировал это - и CTE, и не-CTE (где запрос вводился для каждого экземпляра объединения) занимали ~ 31 секунду. CTE сделал код более читабельным - сократил его с 241 до 130 строк, что очень приятно. Таблица Temp, с другой стороны, сократила его до 132 строк, и на выполнение потребовалось ПЯТЬ СЕКУНД. Без шуток. все это тестирование было кэшировано - все запросы до этого выполнялись несколько раз.

person user2989981    schedule 09.06.2014

Исходя из моего опыта работы с SQL Server, я нашел один из сценариев, в котором CTE превосходит таблицу Temp.

Мне нужно было использовать DataSet (~ 100000) из сложного запроса только ОДИН РАЗ в моей хранимой процедуре.

  • Таблица Temp вызвала накладные расходы на SQL, где моя процедура выполнялась медленно (поскольку таблицы Temp - это реальные материализованные таблицы, которые существуют в tempdb и Persist на протяжении всей моей текущей процедуры)

  • С другой стороны, с CTE CTE сохраняется только до тех пор, пока не будет запущен следующий запрос. Итак, CTE - это удобная структура в памяти с ограниченной областью действия. CTE по умолчанию не используют tempdb.

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

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO
person Amardeep Kohli    schedule 07.09.2015
comment
Ваш ответ кажется очень общим ... Как вы измеряете, что CTE превосходит таблицу Temp? Есть ли у вас измерения времени? На мой взгляд, вам следует отредактировать свой ответ и добавить больше деталей. - person Il Vic; 07.09.2015
comment
Да, у меня есть замеры времени и план выполнения, подтверждающие мое заявление. - person Amardeep Kohli; 07.09.2015
comment
Невозможно добавить img для плана выполнения из-за ограниченных прав. Информация обновится после его разрешения. - person Amardeep Kohli; 07.09.2015