Какие из них более производительны CTE
или Temporary Tables
?
Какие из них более производительны CTE
или Temporary Tables
?
Ответы:
Я бы сказал, что это разные понятия, но не слишком разные, чтобы сказать «мел и сыр».
Временная таблица хороша для повторного использования или для выполнения нескольких проходов обработки для набора данных.
CTE может использоваться либо для рекурсии, либо для просто улучшения читабельности.
И, подобно представлению или встроенной табличной функции, функция также может рассматриваться как макрос, который нужно развернуть в основном запросе.
Временная таблица - это еще одна таблица с некоторыми правилами в области видимости.
Я сохранил процы, где я использую оба (и табличные переменные тоже)
cte vs temporary tables
так что ИМХО этот ответ должен лучше подчеркнуть недостатки CTE. TL; DR связанного ответа: CTE никогда не должен использоваться для производительности. , Я согласен с этой цитатой, поскольку я испытал недостатки CTE.
Это зависит.
Прежде всего
Что такое общее табличное выражение?
(Не рекурсивный) 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
Обратите внимание, что в приведенном выше плане нет упоминания о 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 или производных таблиц . Однако статистика по этому вопросу не создается, и даже если число строк в буфере должно было сильно отличаться от предполагаемого, невозможно выполнить динамическую адаптацию плана выполнения в процессе выполнения (по крайней мере, в текущих версиях. Адаптивные планы запросов могут стать возможными в будущее).
CTE имеет свое применение - когда данные в CTE невелики и наблюдается значительное улучшение читабельности, как в случае с рекурсивными таблицами. Однако его производительность, безусловно, не лучше, чем у табличных переменных, и когда кто-то имеет дело с очень большими таблицами, временные таблицы значительно превосходят CTE. Это связано с тем, что вы не можете определять индексы в CTE и когда у вас большой объем данных, который требует объединения с другой таблицей (CTE просто как макрос). Если вы объединяете несколько таблиц с миллионами строк записей в каждой, CTE будет работать значительно хуже, чем временные таблицы.
Временные таблицы всегда находятся на диске - поэтому, пока ваш CTE может храниться в памяти, он, скорее всего, будет быстрее (как и переменная таблицы).
Но с другой стороны, если нагрузка на данные вашего CTE (или переменной временной таблицы) становится слишком большой, она также будет храниться на диске, так что нет большой выгоды.
В общем, я предпочитаю CTE, а не временную таблицу, так как она ушла после того, как я ее использовал. Мне не нужно думать о том, чтобы отбросить это явно или что-то еще.
Таким образом, нет ясного ответа в конце, но лично я предпочел бы CTE по временным таблицам.
Таким образом, запрос, который мне было поручено оптимизировать, был написан с двумя CTE на сервере SQL. Это заняло 28 секунд.
Я потратил две минуты на преобразование их во временные таблицы, а запрос занял 3 секунды.
Я добавил индекс во временную таблицу на поле, к которому он присоединялся, и уменьшил его до 2 секунд.
Три минуты работы и теперь он работает в 12 раз быстрее, удалив CTE. Лично я не буду использовать CTE, если их сложнее отлаживать.
Сумасшедшая вещь в том, что CTE использовались только один раз, и при этом индекс на них оказался на 50% быстрее.
CTE не займет никакого физического пространства. Это просто набор результатов, который мы можем использовать join.
Временные таблицы являются временными. Мы можем создавать индексы, ограничения как обычные таблицы, для этого нам нужно определить все переменные.
Область действия временной таблицы только в пределах сеанса. EX: открыть окно двух запросов SQL
create table #temp(empid int,empname varchar)
insert into #temp
select 101,'xxx'
select * from #temp
Запустите этот запрос в первом окне, затем запустите приведенный ниже запрос во втором окне, чтобы найти разницу.
select * from #temp
Я использовал оба, но в массивных сложных процедурах всегда находил временные таблицы лучше работать и более методичным. CTE имеют свое применение, но обычно с небольшими данными.
Например, я создал sprocs, которые возвращаются с результатами больших вычислений за 15 секунд, но конвертируют этот код для запуска в CTE и наблюдают, что он выполняется более 8 минут для достижения тех же результатов.
Поздно на вечеринку, но ...
Среда, в которой я работаю, сильно ограничена, поддерживает некоторые продукты вендоров и предоставляет услуги «с добавленной стоимостью», такие как отчетность. Из-за ограничений политики и контрактов, мне обычно не позволяют роскошь отдельного пространства таблиц / данных и / или возможность создавать постоянный код [он становится немного лучше, в зависимости от приложения].
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 (пока я не получу свое пространство данных).
Одно из применений, где я нашел превосходную производительность CTE, было то, где мне нужно было объединить относительно сложный запрос с несколькими таблицами, каждая из которых имела несколько миллионов строк.
Я использовал CTE, чтобы сначала выбрать подмножество на основе индексированных столбцов, чтобы сначала сократить эти таблицы до нескольких тысяч соответствующих строк в каждой, а затем присоединил CTE к моему основному запросу. Это экспоненциально сократило время выполнения моего запроса.
В то время как результаты для CTE не кэшируются, и переменные таблицы, возможно, были лучшим выбором, я действительно просто хотел опробовать их и найти соответствие описанному выше сценарию.
Это действительно открытый вопрос, и все зависит от того, как он используется и от типа временной таблицы (переменная таблицы или традиционная таблица).
Традиционная временная таблица хранит данные во временной базе данных, что замедляет временные таблицы; однако табличных переменных нет.
Я только что проверил это - и CTE, и не-CTE (где запрос был напечатан для каждого экземпляра объединения) заняли ~ 31 секунду. CTE сделал код намного более читабельным, хотя сократил его с 241 до 130 строк, что очень хорошо. С другой стороны, временная таблица сократила его до 132 строк и взяла на запуск ПЯТЬ СЕКУНД. Не шутка. все это тестирование было кэшировано - все запросы выполнялись несколько раз.
Исходя из своего опыта работы с 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