Преимущества Common Table Expression (CTE)?

21

Из MSDN :

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

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

Если я ссылаюсь на CTE несколько раз в одном запросе:

  • Есть ли какой-то выигрыш в производительности?
  • Если я делаю самостоятельное соединение, будет ли SQL Server сканировать целевые таблицы дважды?
Ройи Намир
источник
2
Профилировщик должен сказать вам, если он сканирует дважды. ИМХО, CTE отлично подходят для рекурсии.
Дэн Эндрюс
3
Нет жестких ответов, когда оптимизатор запросов находится в игре. Некоторые запросы увидят преимущества в производительности, а некоторые нет. Иногда использование временной таблицы вместо CTE будет быстрее, иногда - нет.

Ответы:

25

Как правило, CTE НИКОГДА не улучшит производительность .

CTE по сути одноразовый вид. Здесь не хранятся дополнительные статистические данные, индексы и т. Д. Он работает как сокращение для подзапроса.

По моему мнению, они могут быть легко перегружены (я вижу много перерасхода кода в моей работе). Здесь есть несколько хороших ответов, но если вам нужно сослаться на что-то более одного раза, или если это несколько сотен тысяч строк, поместите это в #tempтаблицу и проиндексируйте.

JNK
источник
3
Согласен. За исключением рекурсивных CTE, они просто помогают читабельности
gbn
Что если CTE вернет только несколько строк (чтобы их можно было хранить в памяти), которые дорого рассчитать (агрегирование в большой таблице), и этот результат будет использоваться более одного раза? Это должно улучшить производительность, не так ли? (по крайней мере, это мой опыт работы с PostgreSQL и Oracle, где временные таблицы используются очень редко)
a_horse_with_no_name
2
@a_horse_with_no_name - это было бы эквивалентно тому, чтобы сделать его подзапросом. Если результат используется более одного раза в одном запросе, он будет использован повторно и не будет пересчитан. Если он используется более чем в одном запросе, то a CTEявляется плохим выбором, поскольку результаты отбрасываются после первого запроса.
JNK
@JNK: спасибо. Кажется, SQL Server ведет себя по-другому здесь.
a_horse_with_no_name
Некоторые люди считают CTE более читабельным при определенных обстоятельствах. FWIW stackoverflow.com/a/11170918/32453
rogerdpack
14

Помимо рекурсии, я считаю, что CTE невероятно полезны при создании сложных отчетов. Я использую серию CTE, чтобы получить фрагменты нужных мне данных, а затем объединить в окончательном выборе. Я нахожу, что их легче поддерживать, чем делать одно и то же с большим количеством производных таблиц или 20 объединений, и я могу быть уверен, что он вернет правильные данные без влияния нескольких записей из-за отношений один-много в все разные соединения. Позвольте мне привести быстрый пример:

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

Таким образом, выделив различные порции информации, которую вы хотите, вы можете проверить каждую часть в отдельности (используя закомментированные варианты выбора, раскомментировав каждую из них по отдельности и выполняя только эту выборку), и если вам нужно внести изменения в расход Расчет (в этом примере) найти легче, чем когда все они объединены в один массивный запрос. Конечно, фактические запросы отчетов, для которых я использую это, обычно намного сложнее, чем в примере.

HLGEM
источник
1
Просто для отправки запросов? Системы, над которыми я работаю каждый день, имеют сложные запросы транзакций. Как ни странно, наши отчётные запросы часто являются одними из самых простых. (Конечно, исключая тривиальные CRUD-запросы без соединения).
Кевин Кэткарт
Я использовал это в качестве примера, потому что они, как правило, самые сложные здесь
HLGEM
+1 иногда более логичный (удобочитаемый) запрос предпочтительнее потенциально более эффективного.
понедельник,
Да. Учитывая, что CTE обычно создает один и тот же результирующий план, я не вижу причин создавать ужасно вложенные, чудовища из нескольких подзапросов - когда вместо этого мы можем визуально расположить каждый компонент в том порядке, в котором они необходимы. Я импортирую XML-файлы и выполняю различные акробатические операции, чтобы привести данные в правильную форму, которую было бы невыносимо писать / читать без CTE. (Некоторые из моего старого кода, вероятно, имеют ужасные подзапросы со всех сторон!)
underscore_d
0

Как всегда это зависит, но бывают случаи, когда производительность значительно улучшается. Я вижу это с помощью операторов INSERT INTO SELECT, где вы используете CTE для выбора, а затем используете его в INSERT INTO. Возможно, это связано с тем, что для базы данных установлен RCSI, но в тех случаях, когда выбрано очень мало, это может очень помочь.

Рон С
источник