CTE, Alt Sorgu, Geçici Tablo veya Tablo Değişkeni arasında bir performans farkı var mı?


222

Bu mükemmel SO sorusundaCTE ve arasındaki farklar sub-queriestartışıldı.

Özellikle sormak istiyorum:

Hangi durumlarda aşağıdakilerin her biri daha verimli / daha hızlıdır?

  • CTE
  • Alt Sorgu
  • Geçici Tablo
  • Tablo Değişkeni

Geleneksel olarak, çok sayıda iç içe geçmiş alt sorgudan daha okunabilir göründüklerinden temp tables, geliştirmede çok kullandım stored procedures.

Non-recursive CTEs veri kümelerini çok iyi bir şekilde içine alır ve çok okunabilirdir, ancak her zaman daha iyi performans göstereceklerini söyleyebilecek belirli durumlar var mı? ya da en verimli çözümü bulmak için her zaman farklı seçeneklerle uğraşmak zorunda mıyız?


DÜZENLE

Son zamanlarda, verimlilik açısından, geçici tabloların ilişkili bir histogram yani istatistiklere sahip oldukları için iyi bir ilk seçenek olduğu söylendi.


4
Genel cevap: duruma göre değişir. Ve bazı birçok faktöre bağlıdır, herhangi bir genel ifade muhtemelen yanlıştır - bazı durumlarda. Temel olarak: test etmeniz ve ölçmeniz gerekir - hangisinin sizin için en uygun olduğuna bakın!
marc_s

@marc_s - tamam; belki bu soru sübjektif olduğu için kapatılmalıdır? SO üzerinde SQL soruları bir sürü öznel olarak değerlendirilebilir unutmayın.
whytheq

1
Çok geniş olarak kapalı olsun belki - ve sana katılıyorum - ait SQL şeyler ve konuların bir sürü gerçekten bir cevap alacak duruma göre değişir . Bazen bir karar vermek için iki veya üç kriter listelenebilir, ancak burada sorunuzla, sağlam tavsiye vermek imkansızdır - çok fazla bağlıdır - tablo yapılarınız, bu tablolardaki veriler, kullandığınız sorgular, indeksleme stratejiniz ve çok daha fazlası ....
marc_s

@marc_s denemek ve tutmak iyi olurdu - OP'yi daha spesifik ve dar hale getirmek için olası düzenlemeler hakkında herhangi bir tavsiye?
whytheq

Lütfen bu sorunun SQL Server'a özel olduğunu unutmayın. Postgres gibi diğer DB'ler için, CTE genellikle eşdeğer alt sorgulardan çok daha yavaştır (bkz. Http://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/ )
Jay

Yanıtlar:


243

SQL, bildirimsel bir dildir, yordamsal bir dil değildir. Yani, istediğiniz sonuçları tanımlamak için bir SQL deyimi oluşturursunuz. SQL motoruna işin nasıl yapılacağını söylemiyorsunuz .

Genel bir kural olarak, SQL motorunun ve SQL optimizer'ın en iyi sorgu planını bulmasına izin vermek iyi bir fikirdir. Bir SQL motoru geliştirmek için yıllarca süren çabalar vardır, bu yüzden mühendislerin nasıl yapabileceklerini bildiklerini yapmasına izin verin.

Tabii ki, sorgu planının optimal olmadığı durumlar vardır. Daha sonra, daha iyi performans elde etmek için sorgu ipuçlarını kullanmak, sorguyu yeniden yapılandırmak, istatistikleri güncellemek, geçici tablolar kullanmak, dizinler eklemek vb.

Sorunuza gelince. CTE'lerin ve alt sorguların performansı teorik olarak aynı olmalıdır çünkü her ikisi de sorgu optimize ediciye aynı bilgileri sağlar. Bir fark, bir kereden fazla kullanılan bir CTE'nin bir kez kolayca tanımlanıp hesaplanabilmesidir. Daha sonra sonuçlar depolanabilir ve birden çok kez okunabilir. Ne yazık ki, SQL Server bu temel optimizasyon yönteminden yararlanmak gibi görünmüyor (bu ortak alt sorgu ortadan kaldırması diyebilirsiniz).

Geçici tablolar farklı bir konudur, çünkü sorgunun nasıl çalıştırılacağı konusunda daha fazla rehberlik sağlarsınız. Önemli bir fark, optimize edicinin sorgu planını oluşturmak için geçici tablodaki istatistikleri kullanabilmesidir. Bu performans artışlarına neden olabilir. Ayrıca, bir kereden fazla kullanılan karmaşık bir CTE'niz (alt sorgu) varsa, bunu geçici bir tabloda saklamak genellikle performans artışı sağlar. Sorgu yalnızca bir kez yürütülür.

Sorunuzun yanıtı, özellikle düzenli olarak yürütülen karmaşık sorgular için beklediğiniz performansı elde etmek için oynamanız gerektiğidir. İdeal bir dünyada, sorgu optimize edici mükemmel yürütme yolunu bulur. Sık sık yapsa da, daha iyi performans elde etmenin bir yolunu bulabilirsiniz.


11
Bu alanda ileride gelişmeler Bazı Microsoft Research "Sorgu İşleme için benzer alt ifadelerin Verimli Sömürü” yayında olduğunu buradan Temin
Martin Smith

3
Bu makalenin 2007'de sunulduğu göz önüne alındığında, SQL Server 2012'ye dahil edip etmedikleri hakkında herhangi bir fikir var mı?
Gordon Linoff

3
Harika bir cevap! Sadece vurgulamak için: SQL bildirimsel bir dildir ve verilerin NASIL alındığını kontrol etmiyoruz. Bu nedenle, performans / hız sorgudan sorguya değişir.
Simcha Khabinsky

2
@RGS. . . Geçici tablolardaki dizinler, kalıcı bir tablodaki dizinlerde olduğu gibi, bu dizinlerden yararlanabilecek sorguları kesinlikle geliştirir. Ancak, bir alt sorguyu geçici tablo olarak gerçekleştirirseniz, özgün tablolardaki dizinlerin avantajını kaybedebilirsiniz.
Gordon Linoff

2
@RGS. . Bir veritabanı motoru, karmaşık bir sorguyu yürütme sırasında bir alt sorguyu / CTE'yi gerçekleştirdiğinde, materyalizasyona dizinler eklemez. Geçici tabloları kullanarak bunu manuel olarak yapabilirsiniz.
Gordon Linoff

77

Kural yok. Ben CTEs daha okunabilir bulmak ve bunları kullanmak sürece onlar gerçek sorunu araştırmak yerine tahminim CTE sorun olduğunu ve farklı bir yaklaşım kullanarak yeniden yazmış deneyin bu durumda bazı performans sorununu, sergilerler. Sorunda genellikle niyetimi sorgu ile bildirmek için seçtiğim yoldan daha fazlası var.

CTE'leri çözebileceğiniz veya alt sorguları kaldırabileceğiniz ve bunları #temp tablosu ile değiştirebileceğiniz ve süreyi azaltabileceğiniz durumlar kesinlikle vardır. Bunun nedeni, eski istatistikler, hatta doğru istatistikler elde edememe (örneğin, tablo değerli bir işleve katılma), paralellik veya hatta sorgunun karmaşıklığı nedeniyle en uygun planın üretilememesi ( bu durumda onu parçalamak optimize ediciye bir savaş şansı verebilir). Ancak, #temp tablosu oluşturmakla ilgili G / Ç'nin, CTE'yi daha az çekici kullanarak belirli bir plan şeklini oluşturabilecek diğer performans yönlerinden ağır basabileceği durumlar da vardır.

Dürüst olmak gerekirse, sorunuza "doğru" bir cevap vermek için çok fazla değişken var. Bir sorgunun ne zaman bir yaklaşım veya başka bir lehine ne zaman bahşedileceğini bilmenin öngörülebilir bir yolu yoktur - teorik olarak, bir CTE veya tek bir alt sorgu için aynı semantiğin aynısını yürütmesi gerektiğini bilin . Bunun doğru olmadığı bazı durumlar sunduğunuzda sorunuzun daha değerli olacağını düşünüyorum - optimize edicide bir sınırlama keşfetmiş olabilirsiniz (veya bilinen bir tane keşfetmiş olabilirsiniz) veya sorgularınız anlamsal olarak eşdeğer olmayabilir veya bu optimizasyonu engelleyen bir öğe içeriyorsa.

Bu nedenle, sorguyu sizin için en doğal görünen bir şekilde yazmanızı ve yalnızca optimize edicinin yaşadığı gerçek bir performans sorununu keşfettiğinizde sapmanızı öneririm. Şahsen ben onları CTE, sonra alt sorgu, rütbe #temp tablo son çare olmak.


4
+1 oldukça öznel bir soru olarak ortaya çıkıyor; Umarım şimdiye kadar verilen cevaplar bilgilendirici olduğu için çok belirsiz olduğu için kapanmaz. Sorular değiştiğinde hoşunuza gitmediğini biliyorum ama OP'deki soruyu daraltmak için herhangi bir öneriniz var mı?
whytheq

2
Bu sorunun iyi olduğunu düşünüyorum, henüz kapanacak tek bir oy olmadığını fark edeceksiniz, ancak cevaplar çılgınca sallanmaya başlarsa, muhtemelen kapanacaktır. Cevabımda önerdiğim gibi, bir CTE ve bir alt sorgu arasında büyük bir fark gördüğünüz belirli bir vakanız varsa , gerçek sorgular ve yürütme planları ile yeni bir soru başlatın (ve dba.se'ye daha uygun olabilir ) . Bu sorguya yardımcı olacak yanıtın, aynı senaryoya sahip farklı bir sorgu için aynı yanıt olmayabileceğini unutmayın.
Aaron Bertrand

Sorunuzun hemen altında bağlantılar var link / edit / close / flag- soruyu kapatmak için herhangi bir oy varsa, sorunuzu kapatmak için oy veren kullanıcı sayısını close (n)nerede ntemsil ettiğini göreceksiniz . Bağlantıya tıklarsanız, bu kullanıcıların seçtiği nedenleri görürsünüz.
Aaron Bertrand

@whytheq ayrıca Bob Beauchemin'in bu son blog yayınına da bakınız . CTE'ye ve alt sorguya özel olarak davranmaz, ancak aynı tür bir kavram geçerlidir: performans nedenlerinden dolayı sezgisel olmayan bir desen seçerseniz, boktan belgeyi belgeleyin ve keşfettiğiniz tuhaflığın hala gerçek olduğundan emin olmak için tekrar ziyaret edin. Önceki sürümü tutan güvenilir bir kaynak kontrol sistemi yoksa, sorgunun daha doğal sürümünü yorum bırakmayı bile önerebilirim.
Aaron Bertrand

1
Yukarıdaki sabit bağlantı: sqlskills.com/blogs/bobb/…
ADJenks

19

#temp materalize edilmiş ve CTE değil.

CTE sadece sözdizimidir, bu yüzden teoride sadece bir alt sorgudur. Yürütülür. #temp gerçekleştirildi. Bu nedenle, birçok kez yürütülen bir birleşimde pahalı bir CTE #temp'de daha iyi olabilir. Öte yandan, kolay bir değerlendirme yapılmazsa, ancak birkaç kez #temp yüküne değmez.

SO üzerinde tablo değişkeni sevmiyorum bazı insanlar ama onları gibi #temp daha somutlaştırılmış ve daha hızlı oluşturmak gibi. Sorgu iyileştiricinin #temp ile tablo değişkenine göre daha iyi yaptığı zamanlar vardır.

#Temp veya table değişkeninde PK oluşturma yeteneği, sorgu optimizer'a CTE'den daha fazla bilgi verir (CTE'de PK bildiremezsiniz).


"TVP" kısaltması nedir ... #temp benzeri bir şey var mı?
whytheq

TVP yaygın bir terim haline geliyor, çünkü (bazıları için) etkileyici geliyor. Kısacası, TVP parametre olarak iletilen bir tablodur. Tablo değişkenlerini kullanan herkes evde onlarla birlikte haklı olacaktır.
WonderWorker

1
UYARI - TVP'lerin yürütme planları yoktur! TVP'leri en basit kısa arama listeleri için kullanmayın. Üzerinde karmaşık birleştirmeler, eklemeler veya güncellemeler yaparsanız, büyük optimizasyon sorunlarıyla karşılaşabilirsiniz. Güven bana, bundan yanmıştım.
Heliac

12

Sadece 2 şey her zaman bir CTE yerine # Temp Tablo kullanmak her zaman tercih edilir yapmak düşünüyorum:

  1. CTE'ye birincil anahtar koyamazsınız, böylece CTE tarafından erişilen veriler, geçici tablodaki PK veya Dizin'e erişmek yerine CTE tablolarındaki dizinlerin her birini çaprazlamak zorunda kalacaktır.

  2. Bir CTE'ye kısıtlamalar, dizinler ve birincil anahtarlar ekleyemediğiniz için, sürünen hatalara ve kötü verilere daha eğilimlidirler.


-bugün dün

İşte #table kısıtlamalarının CTE'lerde olmayan kötü verileri önleyebileceği bir örnek

DECLARE @BadData TABLE ( 
                       ThisID int
                     , ThatID int );
INSERT INTO @BadData
       ( ThisID
       , ThatID
       ) 
VALUES
       ( 1, 1 ),
       ( 1, 2 ),
       ( 2, 2 ),
       ( 1, 1 );

IF OBJECT_ID('tempdb..#This') IS NOT NULL
    DROP TABLE #This;
CREATE TABLE #This ( 
             ThisID int NOT NULL
           , ThatID int NOT NULL
                        UNIQUE(ThisID, ThatID) );
INSERT INTO #This
SELECT * FROM @BadData;
WITH This_CTE
     AS (SELECT *
           FROM @BadData)
     SELECT *
       FROM This_CTE;

3
ALWAYSbiraz fazla ama cevap için teşekkürler. Okunabilirlik açısından CTE'lerin kullanımı iyi bir şey olabilir.
whytheq

3
İkinci noktanı hiç anlamıyorum. Gördüğüm şekilde, CTE'yi tanımlayan sorgu, geçici tabloya koyacağınız kısıtlamalara benzer, birincisinin keyfi olarak karmaşık tahminler içerebileceğine dikkat çekerken, ikincisi çok daha sınırlıdır (örneğin CHECK, birden çok satıra / tabloya gönderme yapan kısıtlama izin verilmedi). CTE'nin geçici tablo eşdeğerinde olmayan bir hata sergilediği bir örnek gönderebilir misiniz?
oneday7
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.