Загальні переваги табличної виразності (CTE)?


21

Від msdn :

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

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

Якщо я посилаюсь на CTE кілька разів в одному запиті:

  • Чи є якась користь від продуктивності?
  • Якщо я роблю самостійне приєднання, чи сканує SQL Server цільові таблиці двічі?

2
Профілер повинен повідомити, чи сканується двічі. ІМХО, СТЕ є приголомшливими для рекурсії.
Ден Ендрюс

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

Відповіді:


25

Як правило, CTE НІКОЛИ не покращить продуктивність .

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

На мою думку, їх можна ЛЕГКО зловживати (я бачу багато надмірного використання коду у своїй роботі). Тут є кілька хороших відповідей, але якщо вам потрібно посилатися на щось більше, ніж один раз, або це більше кількох сотень тисяч рядків, замістіть його в #tempтаблицю та індексуйте.


3
Погодьтеся. За винятком рекурсивних CTE, вони просто сприяють
читанню

Що робити, якщо 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

Деякі люди знаходять КТР є більш читабельним при певних обставинах 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

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


1
Тільки для повідомлення про запити? Системи, над якими я працюю щодня, мають такі складні запити транзакцій. Як не дивно, наші запити щодо звітування часто є більш простими. (Звичайно, виключаючи тривіальні безпроблемні запити CRUD).
Кевін Кеткарт

Я використав це як приклад, тому що тут, як правило, найскладніше
HLGEM

+1 іноді більш логічний запит (для читання людини) бажаний запит, ніж потенційно краще.
одного дня, коли

Так. Зважаючи на те, що CTE зазвичай виробляє той самий результат, що склався, я не бачу причин створювати жахливо вкладені зловмисні монстроси, коли ми могли б замість цього викласти кожен компонент візуально в потрібному для них порядку. Я імпортую XML-файли і роблю різні акробатики, щоб отримати дані в потрібній формі, що було б нестерпно писати / читати без CTE. (Деякі з мого старого коду, напевно, мають жахливі підзапроси у всьому світі!)
підкреслюйте

0

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

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