Влияние на производительность прикованной таблицы CTE и Temp
У меня есть следующий цепной запрос CTE (упрощенно):
;WITH CTE1
AS(
SELECT * FROM TableA
),
CTE2
AS(
SELECT * FROM TableB b INNER JOIN CTE1 c ON b.id = c.id
)
SELECT * FROM CTE2
Если я разорву цепочку CTE и сохраню данные CTE1 во временную таблицу, производительность всего запроса улучшится (с 1 минуты 20 секунд до 8 секунд).
;WITH CTE1
AS(
SELECT * FROM TableA
)
SELECT * INTO #Temp FROM CTE1
;WITH CTE2
AS(
SELECT * FROM TableB b INNER JOIN #Temp c ON b.id = c.id
)
SELECT * FROM CTE2
DROP TABLE #Temp
Есть сложные запросы в CTE1 и CTE2. Я только что создал упрощенную версию, чтобы объяснить здесь.
Должен ли сломанный стул CTE улучшить производительность?
Версия SQL Server: 2008 R2
3 ответа
Очевидно, что может, как вы сами показали.
Зачем? Наиболее очевидная причина в том, что оптимизатор знает размер временной таблицы. Это дает ему больше информации для оптимизации запроса. CTE - только оценка. Итак, улучшение, которое вы видите, связано с планом запроса.
Другая причина может заключаться в том, что на запрос ссылается CTE несколько раз. SQL Server не материализует CTE, поэтому код определения будет выполняться несколько раз.
Иногда вы намеренно материализуете CTE как временные таблицы, чтобы в них можно было добавлять индексы. Это также может улучшить производительность.
При всем этом я предпочитаю избегать временных таблиц. Оптимизатор обычно довольно хорош.
Считайте, что cte1 стоит дорого
;WITH CTE1
AS(
SELECT * FROM TableA
)
SELECT * INTO #Temp FROM CTE1
Выше гарантий cte1 запускается только один раз.
Связанный cte может оценивать cte1 несколько раз.
И даже с #temp вы должны рассмотреть index / PK и отсортировать вставку.
Это зависит от многих факторов. Всегда старайтесь написать единственное утверждение, если можете. Преждевременная оптимизация является корнем зла.
Если у вас возникли проблемы с производительностью, вот некоторые из преимуществ разложения вашего единственного утверждения:
- Это может повысить ремонтопригодность, которая является одним из многих нефункциональных требований, за счет уменьшения сложности.
- Это может дать лучший план, если стоимость промежуточной материализации и сэкономленное время меньше первоначальной стоимости.
- Промежуточные таблицы могут быть проиндексированы.
- Индексы, первичные ключи и уникальные ограничения очень полезны для оптимизатора не только для выбора типов соединений, но и для оценки количества элементов, что оказывает большое влияние на предоставление памяти.
- Можно выбрать применение подсказок оптимизатора, таких как MAXDOP, только для выбора операторов, а не одного гигантского оператора. Это особенно полезно, когда вам нужно манипулировать предоставлением памяти.
- Вы можете настроить отдельные операторы для устранения разлива в базу данных.
- В зависимости от сложности и общего времени выполнения вашего процесса вы можете снять блокировку ресурсов раньше, в зависимости также от того, на каком уровне изоляции работают ваши операторы.
- Если ваш план запросов плохой из-за тайм-аута оптимизатора, использование менее сложных отдельных операторов может привести к лучшим общим результатам.