Korzyści z Common Table Expression (CTE)?

21

Z msdn :

W przeciwieństwie do tabeli pochodnej, CTE może być samo-referencyjne i może być wielokrotnie przywoływane w tym samym zapytaniu.

Używam CTE dość często, ale nigdy nie zastanawiałem się głęboko nad korzyściami z ich używania.

Jeśli wielokrotnie odwołuję się do CTE w tym samym zapytaniu:

  • Czy jest jakaś korzyść z wydajności?
  • Jeśli wykonuję samodzielne połączenie, czy SQL Server przeskanuje tabele docelowe dwukrotnie?
Royi Namir
źródło
2
Profiler powinien powiedzieć, czy skanuje dwukrotnie. IMHO, CTE są świetne do rekurencji.
Dan Andrews,
3
Nie ma twardych odpowiedzi, gdy działa optymalizator zapytań. Niektóre zapytania przyniosą poprawę wydajności, inne nie. Czasami użycie tabeli temp zamiast CTE będzie szybsze, a czasem nie.

Odpowiedzi:

25

Z reguły CTE NIGDY nie poprawi wydajności .

CTE jest zasadniczo widokiem jednorazowym. Brak przechowywanych dodatkowych statystyk, indeksów itp. Działa jako skrót dla podkwerendy.

Moim zdaniem można je ŁATWO nadużywać (w mojej pracy widzę dużo nadużywania kodu). Oto kilka dobrych odpowiedzi, ale jeśli chcesz odwołać się do czegoś więcej niż jeden raz lub jest to więcej niż kilkaset tysięcy wierszy, umieść to w #temptabeli zamiast tego i zindeksuj.

JNK
źródło
3
Zgodzić się. Z wyjątkiem rekurencyjnych CTE, to po prostu pomagają w czytelności
gbn
Co się stanie, jeśli CTE zwróci tylko kilka wierszy (aby można je było przechowywać w pamięci), które są drogie do obliczenia (agregacja na dużej tabeli), a ten wynik jest używany więcej niż jeden raz? To powinno poprawić wydajność, prawda? (przynajmniej takie jest moje doświadczenie z PostgreSQL i Oracle, gdzie tabele tymczasowe są używane bardzo rzadko)
a_horse_w_na_nazna
2
@ a_horse_with_no_name - byłoby to równoznaczne z uczynieniem go podzapytaniem. Jeśli wynik zostanie użyty więcej niż jeden raz w jednym zapytaniu, zostanie on ponownie użyty i nie zostanie ponownie obliczony. Jeśli jest używany w więcej niż jednym zapytaniu, to a CTEjest złym wyborem, ponieważ wyniki są odrzucane po pierwszym zapytaniu.
JNK,
@JNK: dzięki. Wygląda na to, że SQL Server zachowuje się tutaj inaczej.
a_horse_w_no_name
Niektórzy ludzie uważają CTE za bardziej czytelne w pewnych okolicznościach FWIW stackoverflow.com/a/11170918/32453
rogerdpack
14

Oprócz rekurencji jednym z miejsc, w którym uważam, że CTE są niezwykle przydatne, jest tworzenie złożonych zapytań sprawozdawczych. Korzystam z serii CTE, aby uzyskać fragmenty potrzebnych mi danych, a następnie łączę się w ostatecznym wyborze. Uważam, że są łatwiejsze w utrzymaniu niż robienie tego samego z wieloma tabelami pochodnymi lub 20 złączeniami, i mogę być bardziej pewny, że zwraca prawidłowe dane bez efektu wielu rekordów ze względu na wiele relacji w wszystkie różne połączenia. Dam szybki przykład:

;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

Tak więc, oddzielając różne części informacji, które chcesz, możesz sprawdzić każdą część osobno (używając skomentowanych wyborów, odkomentowując każdą z osobna i biegając tylko do tego wyboru) i jeśli musisz zmienić koszt obliczenia (w tym przykładzie) łatwiej jest je znaleźć niż wtedy, gdy wszystkie są połączone razem w jedno ogromne zapytanie. Oczywiście faktyczne zapytania raportowe, w których używam tego, są na ogół znacznie bardziej skomplikowane niż w przykładzie.

HLGEM
źródło
1
Tylko do zgłaszania zapytań? Systemy, na których codziennie pracuję, mają skomplikowane zapytania transakcyjne. Co dziwne, nasze zapytania dotyczące raportów są często jednymi z naszych prostszych. (Oczywiście bez trywialnych zapytań CRUD bez dołączania).
Kevin Cathcart,
Użyłem tego jako przykładu, ponieważ te są zwykle najbardziej skomplikowane
HLGEM
+1 czasami bardziej logiczne (czytelne dla człowieka) zapytanie jest lepsze niż potencjalnie lepiej działające.
dniu
Tak. Biorąc pod uwagę, że CTE zwykle tworzy ten sam wynikowy plan, nie widzę powodu, aby tworzyć potwornie zagnieżdżone, wielozadaniowe potworności - kiedy zamiast tego moglibyśmy ułożyć wizualnie każdy element w kolejności, w jakiej są potrzebne. Importuję pliki XML i robię różne akrobacje, aby uzyskać dane we właściwej formie, których zapisywanie / czytanie bez CTE byłoby nie do zniesienia. (Niektóre z mojego starego kodu prawdopodobnie zawierają straszne podkwerendy!)
underscore_d
0

Jak zawsze to zależy, ale są przypadki, w których wydajność jest znacznie poprawiona. Widzę to w instrukcjach INSERT INTO SELECT, w których używasz CTE dla zaznaczenia, a następnie używasz go w INSERT INTO. Może to mieć związek z włączeniem RCSI dla bazy danych, ale w tych czasach, gdy wybrano bardzo niewiele, może to nieco pomóc.

Ron S.
źródło