Bu makalede, SQL 
  Server 2005 ile gelen T-SQL ifadelerinden CTE(Common Table Expression)yi ele 
  alacağız. Ayrıca makalede view ve türetilmiş tablo gibi terimlere de değineceğiz. 
  Bu çerçevede özellikle hiyerarşik veriler için sık sık ihtiyaç duyulan rekürsif 
  sorguların basitleştirilmesini ele alacağız.
  
Giriş 
CTE(Common Table 
  Expressionlar) SQL-99 standardı çerçevesinde Microsoft T-SQLe girmiş bir 
  kalıptır. Bu kalıp, özellikle rekürsif sorguları basitleştirmek için etkin 
  bir yoldur. Genel itibari ile Viewlere ve türetilmiş tablo(Derived Table) 
  lara çok benzeyen bu kalıp, daha çok rekürsif(kendine atıfta bulunan ) sorguları 
  ifade etmeyi kolaylaştırıcı bir etkiye sahiptir
  
Öncelikle, view 
  ve türetilmiş tablo(derived table) kavramlarını birer örnek ile ele alalım:
  
View, bir sorgudan 
  ibaret olan veritabanı öğesi olup, tablo gibi davranır. Veritabanında kapladığı 
  alan bir SELECT… ifadesinden ibarettir. 
  
Basit bir View 
  şu şekilde oluşturululabilir: 
  
Örnek 1: 
  
  
    CREATE 
        VIEW reyon(a,b)  
        AS 
         
             SELECT categoryID, categoryNAme FROM tbCategory
  
       | 
   
 
 
  
Türetilmiş tablolar, 
  alt sorgularla çalışmanın özel bir halidir ve bir sorguda, doğrudan tablo 
  adı vermek yerine, başka bir sorgunun türettiği tabloyu tekrar sorgulamak 
  şeklinde özetleyebiliriz. Bu durumda türetilmiş tabloyu, kaydedilmemiş 
  view gibi düşünebiliriz.
  
Örnek 2: 
  
   
    SELECT 
        reyon .* 
        FROM (  
        SELECT categoryID , categoryName  
        FROM tbCategory )  
        reyon ( a , b )  
        WHERE reyon.a ]] 100
 
  | 
   
 
 
  
Sorgusunda, reyon 
  için türetilmiş tablo denir. Burada, sütun adlarını değiştirdik. (MS Access 
  ile çalışıyorsanız, sütun isimlerinin değiştirilmesine müsaade etmez.)
  
CTE 
Genel Tablo İfadeleri(Common 
  Table Expressions) bir tabloyu ifadeye bağlı tanımlama ve arkasından bu tabloya 
  atıfta bulunacak bir sorgu şeklinde tanımlanabilir. Genel kullanımı şu şekildedir:
  
   
    WITH CTEismi 
        ( sütun-listesi )  
        AS ( CTE tanimi )  
        SELECT|INERT|UPDATE|DELETE… FROM… CTEismi
 
  | 
   
 
Az önceki örneği, 
  CTE ile tekrarlayacak olursak:
  
Örnek-3:
  
   
    WITH reyonCTE 
        ( categoryID , categoryName )  
        AS  
        (  
              SELECT categoryID , categoryName FROM tbCategory 
         
        )  
         
        SELECT reyonCTE . categoryID , reyonCTE . categoryName  
        FROM reyonCTE WHERE reyonCTE . categoryID ]] 100
 
  | 
   
 
 
  
Bir önceki Türetilmiş 
  Tablo ile bu örneğimiz arasındaki tek fark, aynı işin CTE ile yapılması. 
  Ancak bu tür işlemler için, elbette Türetilmiş Tablo, CTEye göre daha 
  sade bir hal almakta. Bu tür durumlar için CTE kullanmak avantaj yerine karmaşıklık 
  getirmekte.
  
Birden fazla CTE 
  aynı sorgu içerisinde kullanılabilir. Bu durum, birden fazla Türetilmiş tablo 
  ile çalışmaya benzer. 
  
 
  
Örnek-4:  
  
  
   
    WITH CTEilkOnReyon 
        (categoryID, categoryName) 
        AS  
        (  
          SELECT TOP 10 categoryID, categoryName  
          FROM tbCategory  
          ORDER BY categoryID  
        ),  
         
        CTEsonOnReyon(categoryID, categoryName)  
        AS  
        (  
          SELECT TOP 10 categoryID, categoryName  
          FROM tbCategory ORDER BY  
          categoryID DESC  
        )  
        SELECT * FROM CTEsonOnReyon, CTEIlkOnReyon 
 
  | 
   
 
Bu sorgu, herhangi 
  iki türetilmiş tabloda olduğu gibi, iki CTE ifadesinin kartezyen çarpımını 
  üretecektir(Biraz saçma ama basit bir örnek). 
  
DİKKAT: 
   
  Birden fazla CTE bir sorguda kullanılacaksa, Bir defa WITH deyimi geçtiğine 
  ve aralarına virgül konularak ayrıdığına dikkat edin. 
  
Yinelemelilik(Recursion) 
  
Türetilmiş tablo 
  kavramından sonra, rekürsif ifade ne demektir biraz da bunun üstünde duralım. 
  Rekürsif ifade, belli bir duruma kadar, sürekli olarak kendisine atıfta bulunan 
  bir sorgu, veri yapısı, fonksiyon veya kod parçası için kullanılan genel bir 
  ifadedir. Bunun için en iyi örnek, faktöriyel hesabı yapan bir fonksiyon 
  olabilir. Bu türden bir C# metodunu şu şekilde kodlayabiliriz: 
  
Burada, dikkat 
  edilirse, bir biriktirme ve bir başlangıç ayarlama olmak üzere, rekürsif 
  işlem iki kısım halinde tanımlanmaktadır.
  
Örnek 5:
  
   
    
public 
        class FaktoriyelHesapla  
        {  
           public int sonuc = 0;  
           public FaktoriyelHesapla(int sayi)  
           {  
              // başlangıç ayarlama  
              sonuc=FaktoriyeliniAl(sayi);  
           }  
         
           private int FaktoriyeliniAl(int sayi)  
           {  
              if(sayi [[ 0 || sayi == 0 || sayi == 1)  
                 return 1;  
              else 
              {  
                 // biriktirme  
                 return sayi * FaktoriyeliniAl(sayi-1); 
         
              }  
           }  
        }
 
  | 
   
 
 İPUCU: 
  Sorguda görüldüğü gibi Rekürsiflik gereksinimi yok ise Sorguyu türetilmiş 
  tablolar kullanarak kurgulamak daha rahat ve okunabilir bir yöntemdir. 
  
  
 
İlişkisel Ortamda 
  Hiyerarşik Veriyi Modellemek 
İlişkisel veritabanında 
  hiyerarşik veri tutmak genellikle problemlidir. Aslında ilişkisel veriyi saklamak 
  bu işin en problemsiz ve en kolay kısmıdır. Şekil.1de bir hiyerarşik verinin 
  ağaç yapısı şeklinde gösterimini görebilirsiniz. 
  
 
  
Şekil.1: Hiyerarşik 
  verilerin ağaç şeklinde gösterimi 
  
Bu türden bir 
  hiyerarşik veriyi tutumak için şu şekilde bir ilişkisel tasarım yapılabilir: 
  
  
(a)  (b) 
   
  
Şekil 2: a) Hiyerarşik 
  veri saklamak için tasarlanmış bir kendisi ile ilişkili tablo. b)Bu tablo 
  içerisinde yer alan hiyerarşik verileri içeren bir sorgunun sonucu
  
Buraya kadar her 
  şey çok pratik. Peki dışarıdan bir kategori kodu geldiğinde bu kategori 
  ve bütün altındaki kategorilere ait ürünleri nasıl bulabiliriz? İşte bu 
  soruya cevap vermek daha önce karşılaşmadınız ise biraz vaktinizi alabilir. 
  Bunun için yapılabileceklerden biri, çoklu ifade ile tablo döndüren bir 
  fonksiyona, bir kategori kodu gönderildiğinde, bu kategori ve alt kategorilerinin 
  listesini tablo olarak döndürtmektir. Bunun için akla gelen ilk çözümlerden 
  birini aşağıda bulabilirsiniz:
  
SQL Server 2000de 
  Rekürsif Sorgu için Örnek Fonksiyon
Örnek-6: 
   
  
  
   
    CREATE 
        FUNCTION FN$CHILDCATSFINDER(  
         
            @categoryID 
        BIGINT  
        )  
        RETURNS @subCatIDs TABLE(categoryID BIGINT, passed BIT)  
        AS  
        BEGIN  
            DECLARE @sonucAdet TINYINT  
         
             
        INSERT INTO @subCatIDs  
         
             
        VALUES(@categoryID,1)  
         
             
        INSERT INTO @subCatIDs  
         
             
        SELECT categoryID,0  
         
             
        FROM tbCategory  
         
             
        WHERE parentCategoryID = @categoryID  
         
             
        SET @sonucAdet = @@ROWCOUNT  
         
             
        WHILE (@sonucAdet != 0)  
        	
          
           
        BEGIN 
         
          
           
          
           
         INSERT 
        INTO @subCatIDs SELECT categoryID,0  
          
           
          
           
         FROM 
        tbCategory WHERE parentCategoryID IN  
          
           
          
           
          
           
         (SELECT 
        categoryID FROM @subCatIDs WHERE passed=0 )  
          
           
          
           
         SET 
        @sonucAdet = @@ROWCOUNT  
          
           
          
           
         UPDATE 
        @subCatIDs  
          
           
          
           
         SET 
        passed=1 
          
           
        END 
         
        RETURN  
        END  
        
 
  | 
   
 
  
Burada yapılan 
  işlemleri özet olarak ele alalım. 
  
Fonksiyonumuz şöyle 
  bir tablo değişken kullanmakta: 
  
categoryID :BIGINT
passed :BIT 
Bu alanlardan categoryIDde 
  kategorinin kodu, passed sütununda da alt kategorilerinin tabloya eklenip eklenmediğini 
  kontrol etmek için ekledik. Bir kategorinin alt kategorileri tabloya eklendiği 
  anda, bu alanı 1e eşitliyoruz. 
  
Bunun dışında 
  yapılanları bir cümle ile özetleyelim: Son sorgudan sıfır kayıt etkileninceye 
  kadar, tablodaki her bir satıra ait alt satırlar tabloya eklenmekte. 
  
Bu fonksiyon oluşturulduktan 
  sonra, bir reyon kodu geldiğinde, bu reyon ve bütün alt reyonlarında yer 
  alan ürünlerin listesini şu şekilde bulabiliriz: 
  
   
     
      SELECT * 
        FROM …. WHERE categoryID IN(SELECT categoryID FROM dbo.FN$CHILDCATSFINDER(1) 
        
 
  | 
   
 
Görüldüğü 
  gibi, rekürsif bir sorguyu, bilindik yöntemlerle gerçekleştirmeye çalışmak 
  oldukça zahmetli bir işlem halini almaktadır. İşte bu türden bir sorun 
  CTE kullanımı için oldukça iyi bir sorundur. Şimdi problemi bir de bu yönü 
  ile inceleyeceğiz.
  
Hiyerarşik Sorgu 
  için CTEnin Rekürsif Kullanımı
Bir CTE kendine 
  referans içerdiğinde rekürsif olur. Bu durum için genel bir kalıp şu şekilde 
  verilebilir:
  
   
     WITH 
        CTEIsim ( sutun listesi )  
        (  
             baslangic sorgusu  
             UNION ALL  
             CTEIsmini çağıran sorgu (biriktirme)  
        )  
        SELECT CTEye referans 
 
  | 
   
 
Örnek-7: 
   
  Kategori ağacını hiyerarşik ve rekürsif sorgulamamız gerekiyorsa: 
  
   
     WITH 
        CatCTE(categoryID, categoryName)  
        AS  
        (  
           SELECT categoryID, categoryName FROM tbCategory  
           WHERE categoryID=1  
               UNION ALL  
           SELECT C.categoryID, categoryName  
           FROM tbCategory C JOIN CatCTE as CTE ON C.parentCategoryID 
        = CTE.categoryID  
        )  
        SELECT * FROM CatCTE 
 
  | 
   
 
 
  
Bu tür bir yapıda, 
  kendini çağırma işlemi, ikinci SELECT boş sonuç seçtiğinde sona erer. 
  Aksi halde rekürsifliğin sonucu olarak sürekli kendisini çağırmaya devam 
  etmesi beklenir. Ancak performansın kırılmaması için SQL Server 2005in default 
  özelliği olarak, bir CTEnin en fazla 100 defa kendini çağırmasına müsaade 
  edilir. Bu sayıyı, artırmak veya azaltmak mümkündür.
  
Örnek -8: 
   
  En fazla 4 seviyeye kadar kendini çağıran bir CTE
  
   
     WITH 
        CatCTE(categoryID, categoryName)  
        AS  
        (  
           SELECT categoryID, categoryName FROM tbCategory  
           WHERE categoryID=1  
              UNION ALL  
           SELECT C.categoryID, categoryName  
           FROM tbCategory C JOIN CatCTE as CTE  
           ON C.parentCategoryID = CTE.categoryID  
        )  
        SELECT * FROM CatCTE OPTION(MAXRECURSION 4)
 
  | 
   
 
DİKKAT 
  
OPTION(MAXRECURSION 
  0) ile tanımlı bir SELECT cümlesi için bir tekrar çağırma sayısı kısıtlaması 
  yoktur. Bu tür bir ifade, ikinci SELECT ifadesi sonuç döndürmez oluncaya 
  kadar kendini çağırma devam eder.
  
Değerlendirme 
  ve Sonuç 
CTE (veya GTI) 
  özellikle rekürsif sorgular veya hiyerarşik veriler sorgulamak için oldukça 
  avantajlı ve kullanımı kolay bir yapı olarak karşımıza çıkmakta. Özellikle 
  Tablo döndüren kullanıcı tanımlı fonksiyonlar(UDF) ile birlikte kullanımı 
  bir çok hiyerarşik sorun için çözüm olacaktır. 
  
                
                Makale: 
                SQL Server 2005 : Recursive(Öz-yinelemeli) Sorgular ve CTE ADO.NET ve SQL Yaşar Gözüdeli 
             |