Разница между CTE и SubQuery?


148

Из этого сообщения Как использовать ROW_NUMBER в следующей процедуре?

Есть две версии ответов, в которых одна использует a, sub-queryа другая - CTEдля решения той же проблемы.

Итак, в чем преимущество использования CTE (Common Table Expression)«подзапроса» (таким образом, более читабельно, что на самом деле делает запрос)

Единственное преимущество использования CTEовера в sub-selectтом, что я могу назвать расширение sub-query. Есть ли какие-либо другие различия между этими двумя, когда CTE используется как простой (нерекурсивный) CTE?


Производный вопрос с хорошим обсуждением: stackoverflow.com/q/11169550/781695
пользователь

8
ИМО, любой, кто думает, что CTE менее читабелен, потому что гигантский кусок переплетенных подзапросов не видел груды запутанных зубчатых запросов, используемых в большинстве систем управления корпоративными данными. Большие, нетривиальные запросы, как правило, значительно легче читать позже или новыми глазами, чем подзапросы, и, по крайней мере, в случае с Postgres во многих случаях волшебным образом выполняется намного лучше. ([По причинам, которые мне еще предстоит понять [( stackoverflow.com/questions/33731068/… ), поскольку противоположное кажется более вероятным.)
zxq9

Ответы:


105

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

В общем ; CTE можно использовать рекурсивно; подзапрос не может. Это делает их особенно подходящими для древовидных структур.


1
Извините, я должен был быть более ясным в своем вопросе. В чем будет разница между CTE и подзапросом в контексте, где CTE используется LIKE subquery?
dance2die 01

2
@Marc Gravell: Мы можем сделать больше, поскольку поведение профилировщика не гарантировано, в отличие от поведения CTE, которое (с точки зрения оценки).
casperOne 01

1
Не уверен, насколько это утверждение имеет смысл для людей, которые смотрят на разницу между CTS и подзапросами - A CTE can be used recursively; a sub-query cannot. Пример был бы отличным.
Аникет Такур

88

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

Однако это не означает, что он выполняется только один раз (как и в предыдущих итерациях этого самого ответа , спасибо всем, кто прокомментировал). Запрос определенно может быть выполнен несколько раз, если на него ссылаются несколько раз; оптимизатор запросов в конечном итоге принимает решение о том, как следует интерпретировать CTE.


«Думайте об CTE как о переменной временной таблицы», означает ли это, что CTE хранится на диске или в памяти?
dance2die 01

Вы не можете использовать CTE или подзапрос в нескольких запросах по определению. Я почти уверен, что оптимизатор обрабатывает подзапрос так же, как и CTE (оценивая набор результатов только один раз, независимо от того, сколько раз он используется в 1 запросе)
AlexCuse 01

@AlexCuse: Думаю, я достаточно прояснил контекст CTE, но я добавил больше, чтобы попытаться уточнить больше.
casperOne 01

@AlexCuse: также не подразумевается, что CTE или подзапрос можно использовать в нескольких местах. Однако разница между CTE и оптимизатором заключается в том, что поведение CTE гарантировано, а поведение оптимизатора - нет.
casperOne 01

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

15

CTEНаиболее полезны для рекурсии:

WITH hier(cnt) AS (
        SELECT  1
        UNION ALL
        SELECT  cnt + 1
        FROM    hier
        WHERE   cnt < @n
        )
SELECT  cnt
FROM    hier

вернет @nстроки (до 101). Полезно для календарей, фиктивных наборов строк и т. Д.

К тому же они более читабельны (на мой взгляд).

Помимо этого, CTEи subqueriesидентичны.


В MSSQL вам нужно добавить точку с запятой (;) перед WITH, в противном случае вы получите ошибку. так и должно быть;WITH blabla AS ...)
Обинна Нненанья 06

2
@ObinnaNnenanya: только если это не первая инструкция в пакете. Нагрузочная ваши заявления с запятой является хорошей идеей в любом случае, даже если SQL Server не обеспечивает его в текущей версии другой , чем раньше WITH, MERGEи подобный
Quassnoi

11

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


8

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

Я предполагаю, что основное отличие - удобочитаемость (я считаю CTE более читабельным, потому что он определяет ваш подзапрос впереди, а не посередине).

И если вам нужно что-то сделать с рекурсией, у вас возникнут проблемы с выполнением этого с помощью подзапроса;)


1
Я не уверен, что есть какая -то неэстетическая разница (хотя я ожидаю, что в определенных ситуациях могут быть небольшие различия в плане выполнения). Хотите просветить меня?
AlexCuse

2
Вы можете называть CTE, но можете только псевдонимы подзапросов. Разница в том, что вы можете повторно использовать CTE с несколькими псевдонимами (см. Пример @Michael Petito в его комментарии к casperOne). Я не знаю, как это сделать с помощью подзапросов.
kmote

7

Один важный факт, о котором никто не упомянул, заключается в том, что (по крайней мере, в postgres) CTE - это средства оптимизации:

https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

То есть они будут рассматриваться как их собственный атомарный запрос, а не складываться во весь план запроса. Мне не хватает опыта, чтобы дать лучшее объяснение, но вам следует проверить семантику для той версии sql, которую вы используете; для опытных пользователей возможность создания ограждения оптимизации может повысить производительность, если вы эксперт в управлении планировщиком запросов; Однако в 99% случаев вам следует избегать попыток указывать планировщику запросов, что делать, потому что то, что, по вашему мнению, будет быстрее, скорее всего хуже, чем то, что он думает, будет быстрее. :-)


6

Добавляя к другим ответам, если один и тот же подзапрос используется несколько раз, вы можете заменить все эти подзапросы одним CTE. Это позволяет вам лучше повторно использовать ваш код.


4

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


2

ПОДСКАЗКА: (МАКСИМАЛЬНОЕ ПРЕДУПРЕЖДЕНИЕ n)

вы можете ограничить количество уровней рекурсии, разрешенных для конкретного оператора, используя MAXRECURSIONподсказку и значение от 0 до 32 767 в OPTIONпредложении

Например, вы можете попробовать:

OPTION 
      (MAXRECURSION 150)

GO
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.