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

136

Какие из них более производительны CTEили Temporary Tables?

Бланкмэн
источник
3
Похожий вопрос: dba.stackexchange.com/q/13112
Рэйчел
Пользователи могут найти справочную информацию (не относящуюся к производительности) на странице « Использование выражений общих таблиц» на technet.microsoft.com.
Шеридан

Ответы:

62

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

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

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

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

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

ГБН
источник
12
Временные таблицы также позволяют использовать индексы и даже статистику, которые иногда необходимы, а CTE - нет.
CodeCowboyOrg
9
Я думаю, что этот ответ недостаточно подчеркивает тот факт, что CTE может привести к ужасной производительности. Я обычно ссылаюсь на этот ответ на dba.stackexchange. Ваш вопрос стоит на втором месте в моей поисковой системе, если я смотрю вверх, cte vs temporary tablesтак что ИМХО этот ответ должен лучше подчеркнуть недостатки CTE. TL; DR связанного ответа: CTE никогда не должен использоваться для производительности. , Я согласен с этой цитатой, поскольку я испытал недостатки CTE.
ТТ.
2
@TT. Интересный. Я считаю, что CTE работают намного лучше
Squ1rr3lz
198

Это зависит.

Прежде всего

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

(Не рекурсивный) 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 строк из 1 000 000 случайно сгенерированных значений соответствуют предикату, но дорогостоящее сканирование таблицы происходит 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 или производных таблиц . Однако статистика по этому вопросу не создается, и даже если число строк в буфере должно было сильно отличаться от предполагаемого, невозможно выполнить динамическую адаптацию плана выполнения в процессе выполнения (по крайней мере, в текущих версиях. Адаптивные планы запросов могут стать возможными в будущее).

Мартин Смит
источник
33
Это единственный ответ, который отвечает на реальный вопрос (который задает вопрос, который имеет лучшую производительность, а не какая разница или какой ваш любимый), и он отвечает на этот вопрос правильно: «Это зависит» - это правильный ответ. Это также единственный ответ с подтверждающими данными, чтобы объяснить, некоторые другие (с большим количеством голосов) делают определенные утверждения, что один лучше, чем другой, без ссылок или доказательств ... Чтобы быть ясно, все эти ответы также неверны , Потому что "Это зависит"
Arkaine55
2
Это также хорошо написанный ответ. Серьезно на высшем уровне.
Дэн Уильямс
50

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

CSW
источник
9
Я видел это на собственном опыте. CTE работают значительно медленнее.
goku_da_master
7
CTE также работают медленнее, потому что результаты не кэшируются. Поэтому каждый раз, когда вы используете CTE, он перезапускает запрос, план и все.
goku_da_master
1
И механизм БД может выбрать повторный запрос не только для каждой ссылки, но и для каждой строки запроса потребителя, как коррелированный подзапрос ... вы должны всегда следить за этим, если это не нужно.
Mike M
Временная таблица хранится в базе данных tempdb на SQL Server, который является дисковым, но имеет преимущество в том, что индексируется, и в этом случае оптимизатор SQL хорошо работает с выбранными запросами. Не уверен, на какой базе данных или в области диска хранится CTE (когда он превышает объем памяти и помещается в очередь для пейджинга ввода-вывода), но он никогда не оптимизируется с большим объемом данных. Я использовал опцию компилятора (с перекомпиляцией) иногда, чтобы сделать это быстрее
rmehra76
33

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

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

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

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

marc_s
источник
2
В случае SQLite и PostgreSQL, временные таблицы будут автоматически удаляются ( как правило , в конце сеанса). Я не знаю о других СУБД, хотя.
Серрано
1
CTE походит на временную картину. Данные AFAIK не сохраняются, поэтому ничто не может храниться в памяти или храниться на диске. Важное замечание: каждый раз, когда вы используете CTE, запрос запускается снова.
Роб
1
Лично я никогда не видел, чтобы CTE работал лучше, чем временная таблица для скорости. И отладка намного проще с временным столом
Марк Монфорти
7

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

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

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

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

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

Марк Монфорти
источник
6

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

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

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

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

select * from #temp

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

select * from #temp
Selvaraj
источник
4
>> "это просто набор результатов, который мы можем использовать." -> Это не точно. CTE - это не «набор результатов», а встроенный код. Механизм запросов SQL Server анализирует код CTE как часть текста запроса и строит план выполнения согласно. Идея, что CTE является встроенной, является большим преимуществом использования CTE, поскольку она позволяет серверу создавать «комбинированный план выполнения»
Ронен Арили
4

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

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

Andy_RC
источник
3

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

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

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

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

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

Марк
источник
3

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

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

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

Перчасом
источник
Кроме того , я думаю , так как я использую только КТР в соединении я только реально выполнить КТР один раз в моем запросе так кэширование результатов не была такой большой проблемой в этом отношении
Перчас
1

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

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

JoshBerke
источник
1

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

user2989981
источник
1

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

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

  • Временная таблица вызывала накладные расходы в SQL, где моя Процедура выполнялась медленно (так как Временные таблицы - это реальные материализованные таблицы, которые существуют в 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
Амардип Кохли
источник
1
Ваш ответ, кажется, очень общий ... Как вы оцениваете эту "CTE превзошла временную таблицу"? У вас есть измерения времени? На мой взгляд, вы должны отредактировать свой ответ и добавить больше деталей.
Il Vic
Да, у меня есть измерения времени и план выполнения, чтобы поддержать мое заявление.
Амардип Кохли
Невозможно добавить img для плана выполнения из-за ограниченных привилегий. Будут обновлены детали после его разрешения
Amardeep Kohli