Які ефективніші, CTE або тимчасові таблиці?


136

Які ефективніші, CTEчи Temporary Tables?


3
Питання, пов’язані з цим: dba.stackexchange.com/q/13112
Рейчел

Користувачі можуть знайти довідкову інформацію (не стосується продуктивності) на сторінці Використання загальних табличних виразів на technet.microsoft.com.
Шерідан

Відповіді:


62

Я б сказав, що це різні поняття, але не надто різні, щоб сказати "крейда і сир".

  • Тимчасова таблиця хороша для повторного використання або для виконання декількох переходів на обробку набору даних.

  • CTE можна використовувати або для повторної чи просто покращеної читабельності.
    І, як функція перегляду або вбудована таблиця, яка оцінюється, також може розглядатися як макрос, який слід розширити в головному запиті

  • Тимчасова таблиця - це ще одна таблиця з деякими правилами щодо сфери застосування

Я зберігав програми, де я використовую і обидва (і змінні таблиці)


12
Тимчасові таблиці також дозволяють вводити індекси та навіть статистику, які іноді необхідні, а CTE - ні.
CodeCowboyOrg

9
Я думаю, що ця відповідь недостатньо висвітлює той факт, що CTE може призвести до жахливих показників. Я зазвичай посилаюся на цю відповідь на dba.stackexchange. Ваше питання з’являється другим у моїй пошуковій системі, якщо я шукаю cte vs temporary tablesтак, що IMHO ця відповідь повинна підкреслити недоліки CTE краще. TL; DR пов'язаної відповіді: CTE ніколи не повинен використовуватися для продуктивності. . Я погоджуюся з цією цитатою, коли я пережив недоліки CTE.
ТТ.

2
@TT. Цікаво. Я вважаю, що CTE працюють набагато краще
Squ1rr3lz

198

Це залежить.

Поперше

Що таке загальний вираз таблиці?

(Нерекурсивний) 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

План 1

Зауважте, що в плані вище немає CTE1. Він просто отримує доступ до базових таблиць безпосередньо і трактується так само, як

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Переписування шляхом матеріалізації 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 або отриманих таблиць . Однак щодо цього не створюється ніяких статистичних даних, і навіть якщо кількість спільних рядків повинна сильно відрізнятися від прогнозованої, неможливо, щоб план виконання проекту динамічно адаптувався у відповідь (принаймні, у поточних версіях. Адаптативні плани запитів можуть стати можливими в майбутнє).


33
Це єдина відповідь, яка відповідає на фактичне запитання (що задає питання, яке має кращі показники, а не те, в чому різниця чи яке ваше улюблене), і він відповідає на це питання правильно: "Це залежить" - це правильна відповідь. Це також єдина відповідь із супровідними даними, що пояснюють, кілька інших (з високою кількістю голосів) висловлюють певні твердження, що один кращий за інший без посилань чи доказів ... Щоб було зрозуміло, всі ці відповіді також неправильні . Тому що "Це залежить"
Arkaine55

2
Це також добре написана, добре посилається відповідь. Серйозно найвищий рівень.
Ден Вільямс

50

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


9
Я це бачив із власного досвіду. CTE працює значно повільніше.
goku_da_master

7
CTE також працюють повільніше, оскільки результати не кешуються. Тож кожен раз, коли ви використовуєте CTE, він повторно виконує запит, план і все.
goku_da_master

1
І двигун db може вибрати для повторного запуску запиту не тільки кожну посилання, але і для кожного рядка запиту споживача, як співвіднесений підзапит ... Ви завжди повинні стежити за цим, якщо він не бажаний.
Майк М

Таблиця temp зберігається в tempdb на SQL Server, який є дисковим, але має перевагу від індексації, а оптимізатор SQL добре працює на обраних запитах у цьому випадку. Не впевнений, в якій зоні db або диска зберігається CTE (коли він перевищує розмір пам'яті і ставиться в чергу для підключення вводу-виводу), але його ніколи не оптимізували при великому обсязі даних. Я використовував варіант компілятора (з перекомпіляцією) іноді, щоб зробити це швидше
rmehra76

33

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

Але знову ж таки, якщо завантаження даних вашої CTE (або змінної темп-таблиці) стає занадто великим, воно також буде збережене на диску, тому великої користі немає.

Як правило, я віддаю перевагу CTE над тимчасовою таблицею, оскільки він минув після того, як я його використав. Мені не потрібно думати про те, щоб явно відмовитись від цього чи чогось.

Отже, жодної чіткої відповіді, врешті-решт, але особисто я вважаю за краще CTE над тимчасовими таблицями.


2
У разі SQLite і PostgreSQL, тимчасові таблиці будуть автоматично видаляються ( як правило , в кінці сеансу). Я не знаю про інші СУБД.
Серрано

1
CTE - це як темп. Дані AFAIK не зберігаються, тому нічого не зберігається в пам'яті та не зберігається на диску. Важлива примітка, щоразу, коли ви використовуєте CTE, запит запускається знову.
Роб

1
Особисто я ніколи не бачив, щоб CTE працював краще, ніж таблиця Temp для швидкості. А також налагодження набагато простіше з тимчасовою таблицею
Марк Монфорті

7

Тож запит, який мені призначили оптимізувати, був написаний з двома CTE на SQL сервері. Це займало 28 сек.

Я витратив дві хвилини на перетворення їх у темп-таблиці і запит зайняв 3 секунди

Я додав індекс до таблиці темп на полі, до якого він приєднувався, і опустив його на 2 секунди

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

Божевільна річ - це те, що CTE обидва були використані лише один раз, і все-таки ставка індексу виявилася на 50% швидшою.


6

CTE не займе жодного фізичного місця. Це просто набір результатів, який ми можемо використовувати приєднання.

Тимчасові таблиці є тимчасовими. Ми можемо створювати індекси, обмежує як звичайні таблиці, для цього нам потрібно визначити всі змінні.

Область застосування таблиці темп лише в межах сеансу. EX: Відкрийте два вікна запиту SQL

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Запустіть цей запит у першому вікні, після чого запустіть запит нижче, у другому вікні ви зможете знайти різницю.

select * from #temp

4
>> "це просто набір результатів, який ми можемо використовувати приєднання." -> Це не точно. CTE - це не "набір результатів", а вбудований код. Система запитів SQL Server розбирає код CTE як частина тексту запиту та будує план виконання відповідно до цього. Ідея про те, що CTE є вбудованою, є великою перевагою використання CTE, оскільки вона дозволяє серверу створити "комбінувати план виконання"
Ronen Ariely

4

Я використовував і те і інше, але в масивних складних процедурах завжди знаходив темп-таблиці, які краще працювати і більш методичними. ЦТЕ використовують, але зазвичай мають невеликі дані.

Наприклад, я створив відростки, які повертаються з результатами великих обчислень за 15 секунд, але конвертують цей код для запуску в CTE, і я бачив, що він працює більше 8 хвилин для досягнення тих же результатів.


3

Пізно на вечірку, але ...

Навколишнє середовище, в якому я працюю, дуже обмежене, підтримуючи деякі продукти постачальників та надаючи послуги з додатковою вартістю, такі як звітність. Через політичні та контрактні обмеження мені зазвичай не дозволено розкіш окремого простору таблиці / даних та / або можливість створювати постійний код [він стає трохи кращим, залежно від програми].

IOW, я зазвичай не можу розробити збережену процедуру, або UDF, або тимчасові таблиці і т. Д. Мені дуже доводиться робити все через інтерфейс мого додатка (Crystal Reports - додавання / посилання таблиць, встановлення де пункти з w / in CR тощо). ). Одне МАЛЕЕ заощадження полягає в тому, що Crystal дозволяє мені використовувати COMMANDS (а також SQL вирази). Деякі речі, які не є ефективними завдяки можливості звичайних таблиць додавання / посилання, можна виконати, визначивши команду SQL. Я використовую CTE через це і даю дуже хороші результати "віддалено". CTE також допомагають обслуговувати звіт / звіт, не вимагаючи розробки коду, передають DBA для компіляції, шифрування, передачі, встановлення, а потім вимагають багаторівневого тестування. Я можу робити CTE через локальний інтерфейс.

Нижньою стороною використання CTE з / п є кожен звіт окремий. Кожен CTE повинен підтримуватися для кожного звіту. Там, де я можу робити SP та UDF, я можу розробити щось, що може використовуватися у кількох звітах, вимагаючи лише посилання на SP та проходження параметрів, як якщо б ви працювали над звичайною таблицею. CR не дуже добре обробляє параметри в SQL-командах, тому цей аспект CR / CTE може бути відсутнім. У цих випадках я зазвичай намагаюся визначити CTE для повернення достатньої кількості даних (але не ВСІХ даних), а потім використовую можливості вибору запису в CR, щоб нарізати та порізати це.

Отже ... моє голосування за CTE (поки я не отримаю простір даних).


3

Одне використання, де я вважав, що CTE виявив чудову ефективність, коли мені потрібно було приєднати порівняно складний Запит до декількох таблиць, які мали по кілька мільйонів рядків у кожній.

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

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


Крім того, я думаю, оскільки я використовую лише CTE для приєднання, я дійсно виконую лише CTE один раз у своєму запиті, тому кешування результатів не було такою великою проблемою в цьому відношенні
покупки

1

Це справді відкрите запитання, і все залежить від того, яким чином його використовувати та тип таблиці temp (таблична змінна або традиційна таблиця).

Традиційна таблиця тимчасових даних зберігає дані у БД temp, що робить сповільнення темп-таблиць; однак змінних таблиці немає.


1

Я щойно тестував це - і CTE, і не-CTE (де запит було введено для кожного примірника об'єднання), і тривало ~ 31 секунди. CTE зробив код набагато більш читабельним, проте скоротив його з 241 до 130 рядків, що дуже приємно. З іншого боку, таблиця темпів скоротила її на 132 рядки та запустила П'ять секунд. Без жартів. все це тестування було кешоване - запити виконувались кілька разів раніше.


1

Зі свого досвіду роботи на SQL сервері я знайшов один із сценаріїв, коли CTE перевершив таблицю темпів

Мені потрібно було використовувати DataSet (~ 100000) зі складного Запиту просто ONCE в моїй збереженій процедурі.

  • Таблиця темпів викликала накладні витрати на SQL, де моя процедура виконувалась повільно (так як таблиці Temp - це реальні матеріалізовані таблиці, які існують у 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

1
Ваша відповідь здається дуже загальною ... Як ви вимірюєте, що "CTE випереджає таблицю темпів"? У вас є кілька вимірювань часу? На мою думку, ви повинні відредагувати свою відповідь та додати більше деталей.
Il Vic

Так, у мене є вимірювання часу та план виконання для підтвердження мого твердження.
Amardeep Kohli

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