Різниця між CTE та SubQuery?


143

З цієї публікації Як використовувати ROW_NUMBER у наступній процедурі?

Існує дві версії відповідей, де одна використовує a, sub-queryа друга використовує a CTEдля вирішення тієї ж проблеми.

Тепер, у чому полягає перевага використання CTE (Common Table Expression)над "підзапитом" (таким чином, читабельніше, що запит насправді робить)

Єдина перевага використання за CTEкадром sub-select, що я можу на насправді назватиsub-query . Чи є якісь інші відмінності між цими двома, коли CTE використовується як простий (нерекурсивний) CTE?



7
IMO, кожен, хто вважає, що CTE є менш зрозумілим, що гігантська пляма переплетених підзапитів не побачила купу сміття плутаючих запитів у формі зубчастих зубів у більшості систем управління даними підприємства. Великі нетривіальні запити, як правило, значно простіше читати пізніше чи новими очима, ніж підзапити, і, принаймні, у випадку Постгресу в багатьох випадках магічні показники значно кращі. ([З причин, які я ще не зрозумів [( stackoverflow.com/questions/33731068/… ), оскільки навпаки здається більш ймовірним.)
zxq9

Відповіді:


102

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

В цілому ; CTE можна використовувати рекурсивно; підзапит не може. Це робить їх особливо гарними для деревних конструкцій.


1
Вибачте, я мав би бути більш чітким у своєму питанні. Яка буде різниця між CTE та Subquery у контексті, коли CTE використовується підзапросом LIKE?
dance2die

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

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

88

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

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


"Подумайте про CTE як змінну часової таблиці", чи означає це, що CTE зберігається на диску або в пам'яті?
dance2die

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

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

@AlexCuse: Немає жодних наслідків, що CTE або підзапрос можна використовувати в декількох місцях. Однак різниця між CTE та оптимізатором полягає в тому, що поведінка CTE гарантована, тоді як поведінка оптимізатора - ні.
casperOne

і я визнаю, що можуть бути деякі крайні випадки, коли оптимізатор задавлюється і підзапит оцінюється не один раз, я не натрапив на жоден. Потім я знову використовую CTE, де можу;)
AlexCuse

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's і subqueriesє тотожними.


У MSSQL вам потрібно додати крапку з комою (;) перед символом WITH, замовити мудро, ви отримаєте помилку. це має бути;WITH blabla AS ...)
Обінна Нненанія

2
@ObinnaNnenanya: лише якщо це не перша заява в партії. Припинення ваших висловлювань крапками з комою - це все-таки хороша ідея, навіть якщо SQL Server не застосовує їх у поточних версіях, окрім попередніх WITH, MERGEта подібних
Quassnoi

10

Однією з різниць, про яку не було сказано, є те, що єдиний CTE може бути посилається на кілька частин об'єднання


8

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

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

І якщо вам потрібно буде щось робити з рекурсією, у вас виникнуть невеликі труднощі, роблячи це з підзапитом;)


1
Я не впевнений, що є якась неестетична різниця (хоча я сподіваюся, що в певних ситуаціях можуть бути незначні відмінності в плані виконання). Хочете просвітити мене?
AlexCuse

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

7

Важливим фактом, про який ніхто не згадував, є те, що (принаймні, в постгресі) CTE є оптимізаційними огорожами:

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

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


6

Додавши відповіді інших, якщо один і той самий підзапит використовується кілька разів, ви можете замінити всі ці підзапити одним CTE. Це дозволяє краще використовувати свій код.


4

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


2

Підказка: (MAXRECURSION n)

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

Наприклад, ви можете спробувати:

OPTION 
      (MAXRECURSION 150)

GO
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.