Какие из них более производительны, CTE или временные таблицы? - PullRequest
125 голосов
/ 27 марта 2009

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

Ответы [ 12 ]

159 голосов
/ 05 октября 2014

Это зависит.

Прежде всего

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

(нерекурсивный) 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

Plan 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 раз, чтобы найти их.

enter image description here

Это было бы хорошим кандидатом для материализации промежуточного результата. Эквивалентная перезапись временной таблицы заняла 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 

With Plan

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

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

62 голосов
/ 30 марта 2009

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

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

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

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

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

47 голосов
/ 30 апреля 2010

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

32 голосов
/ 27 марта 2009

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

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

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

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

6 голосов
/ 30 мая 2011

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

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

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

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

select * from #temp

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

select * from #temp
5 голосов
/ 14 сентября 2016

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

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

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

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

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

4 голосов
/ 01 сентября 2014

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

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

3 голосов
/ 06 сентября 2012

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

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

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

3 голосов
/ 18 мая 2011

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

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

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

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

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

1 голос
/ 07 сентября 2015

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

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

  • Временная таблица вызывала накладные расходы на SQL, где моя процедура выполнять медленно (так как Temp Tables - это реальные материализованные таблицы, которые существует в базе данных 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
...