Sayfalar

24 Ocak 2013 Perşembe

Hiyerarşik Sorgulama

Merhaba Arkadaşlar!

Veriler veritabanında çoğu kez bir ağaç yapısıyla yerleşirler. Örneğin; bir şirketin organizasyon yapısını düşünürseniz, en başta şirketin sahibi, onun altında bölümlerin yöneticileri, onun altında iş birimlerinin yöneticileri ve en altta da çalışanlar şeklinde hiyerarşik bir yapıda dizilirler. Bu bir ağaç yapısıdır. Ağaç yapısında düğümler ve dallar olur. Dallar düğümleri birbirine bağlayan ilişkilerdir. Burada elbette ağaç veri yapısını anlatmayacağım.

Açıklamak istediğimiz nokta şu: Veritabanlarında saklanan veriler arasındaki hiyerarşiyi nasıl ortaya çıkarabiliriz? Örneğin sizden bir şirketin çalışanları arasındaki hiyerarşik yapıyı bulmanız istendi. Bunu nasıl sorgularsınız? Elbette Oracle'ın PL/SQL'i bu duruma bir çözüm getiriyor. Hemen bakalım:

SELECT [LEVEL], sütunlar, ifadeler...
FROM tabloismi
[WHERE sartlar]
[START WITH sartlar]
[CONNECT BY PRIOR sartlar];


Buradaki terimleri inceleyelim:
  • START WITH: Ağacı taramaya nereden başlayacağımızı bu şart ile belirtiriz. Örneğin ismi John olan çalışan ile taramaya başlarken: START WITH first_name = 'John' demeliyiz. 
  • CONNECT BY PRIOR: Tarama işleminin yönünü belirtir. CONNECT BY PRIOR sütun1 = sütun2 şeklinde kullanılır. Tarama yönü iki tanedir:
    • Aşağıdan yukarı: sütun1 olarak çocuk sütun ismi verilir. sütun2 olarak da ebeveyn sütun ismi verilir.                                                                 
    • Yukarıdan aşağısütun1 olarak ebeveyn sütun ismi verilir. sütun2 olarak da çocuk sütun ismi verilir.                 
SELECT employee_id, last_name, job_id, manager_id
FROM   employees
START  WITH  employee_id = 103
CONNECT BY PRIOR manager_id = employee_id ;
EMPLOYEE_ID            LAST_NAME            JOB_ID     MANAGER_ID             
---------------------- -------------------- ---------- ----------
103                    Hunold                    IT_PROG    102                    
102                    De Haan                   AD_VP      100                    
100                    King                      AD_PRES                           

3 rows selected

Yukarıdaki sorguyu ve sonuçlarını ele alalım: Bu sorguda aşağıdan yukarı bir tarama söz konusudur. Çünkü biliyoruz ki employee_id ebeveyn sütundur. cocuksütun = ebeveynsütun yani manager_id = employee_id kullanımı da aşağıdan yukarı taramada söz konusudur. Sonuçları görürsek zaten durumu anlıyoruz. Hunold isimli çalışan 3. seviyede yetkili bir çalışandır. Hunold'un yöneticisi De Haan, De Haan'ın yöneticisi de King'dir. King en yetkili isim olduğu için bir manager_id NULL olarak gelmişitir. Şimdi tersten bir tarama yapalım. Yapılacak işlem şudur. En yetkili isimden; yani King'den başlayarak en alt seviyeye doğru ineceğiz.

SELECT  last_name||','|| 
PRIOR   last_name || ' tarafından yönetilir' AS "Yukarıdan Aşağı Tarama"
FROM    employees
START   WITH last_name = 'King'
CONNECT BY PRIOR employee_id = manager_id ;
Yukarıdan Aşağı Tarama                                
----------------------------------------------
King, tarafından yönetilir                           
King, tarafından yönetilir                           
Kochhar, King tarafından yönetilir                    
Greenberg, Kochhar tarafından yönetilir               
Faviet, Greenberg tarafından yönetilir 
...
108 rows selected

Buradaki PRIOR anahtar kelimesinin kullanımına dikkat ediniz. Taramada o andaki çalışandan daha yetkili olan çalışanı gösterecek şekilde bu anahtar kelime kullanılmıştır. Görüldüğü gibi gayet kullanışlı ve kolay bir PL/SQL özelliğidir. 

LEVEL ve LPAD İle Organizasyon Şeması
Hatırlarsanız sorgunun genel görünümünde LEVEL anahtar kelimesini belirtmiştik. Peki bu ne işe yarıyor? Buradaki level/seviye'den kasıt elbette ağacın düğüm seviyeleridir. Örneğin King en yetkili kişidir ve ağacın ilk düğümünü oluşturur. Bu yüzden 1. seviyededir. Bundan sonraki yetkili kişi olan Kochhar da 2. seviyededir. Bu böylece sürüp gitmektedir. İşte LEVEL anahtar kelimesi de tam olarak bu seviyeleri görmemizi sağlar. Aslında bize basit bir organizasyon şeması çıkartır. Kullanımını görelim:

COLUMN Org_Sema FORMAT A12
SELECT LPAD(last_name, LENGTH(last_name)+(LEVEL*2)-2,' ') 
       AS Org_Sema
FROM   employees 
START WITH first_name='Steven' AND last_name='King' 
CONNECT BY PRIOR employee_id=manager_id;
ORG_SEMA              
----------------------
King                  
  Kochhar             
    Greenberg         
      Faviet          
      Chen            
      Sciarra         
      Urman           
      Popp            
    Whalen 
...
107 rows selected.

Gördüğünüz gibi çalışanların isimleri hiyerarşik bir düzen ile getirildi. Peki bu nasıl oldu? Elbette LEVEL ve LPAD'ın başarılı bir formülle kullanımı ile oldu. LEVEL anahtar kelimesi o andaki satırın hiyerarşik düzende kaçıncı sırada olduğunu bildiriyor. Örneğin King için bu değer 1, Kochhar için 2, Greenberg için de 3'tür.Peki LPAD ne yapıyor? LPAD'ı bir düzenleme fonksiyonu olarak düşünebiliriz. Üç adet argüman alıyor:
LPAD(basilacakifade, karaktersayisi, eklenecekifade)
İlk argüman fonksiyonun yazdıracağı ana string ifadeyi bildiriyor. İkinci argüman ise "bu string ifadenin kaç karak teri basılacak?" sorusunu yanıtlıyor. Son argüman ise eğer karakter sayısı için yer kalmışsa, ifadenin başına kalan adet kadar verilen karakteri basıyor. Bir kaç örnek verelim ki konu anlaşılabilsin:

Fonksiyon                Sonuç
------------------------ ------------
LPAD('abcde', 5)         abcde
LPAD('abcde', 8)            abcde
LPAD('abcde', 8, '_')    ___abcde
LPAD('abcde', 2)         ab
LPAD('abcde', 10, '123') 12312abcde

Bu örnekleri incelerseniz LPAD'ın ne olduğunu anlarsınız. Şimdi sorguya dönelim. İlk üç seviye için fonksiyon aşağıdaki gibi işletilir:

Fonksiyon                         Sonuç
--------------------------------- ----------------
LPAD('King', 4+(1*2)-2, ' ')      King
LPAD('Kochhar', 7+(2*2)-2, ' ')   __Kochhar
LPAD('Greenberg', 9+(3*2)-2, ' ') ____Greenberg

Bu sayede basit bir organizasyon şeması oluşturulmuş oldu.

Düğüm ve Dalları Budama
Eğer bazı sonuçları sorgunuzdan elemek isterseniz bu da mümkündür. Bu işlemi düğüm veya dallar için yapabilirsiniz. Bir düğümü elemek istediğinizde bunu WHERE ifadesi içerisinde belirtirsiniz. Örneğin soy ismi Kochhar olan çalışanı istemiyorsanız:
...WHERE last_name != 'Kochhar'...
Demeniz yeterli. Eğer bir dalı budamak istiyorsanız bunu bu kez CONNECT BY içerisinde belirtmeniz gerekiyor:
...CONNECT BY PRIOR
employee_id = manager_id
AND last_name != 'Kochhar'...


Sorguyu aşağıdaki gibi değiştirirseniz sadece 'Kochhar' isimli çalışanın sonuçtan çıkarıldığını görürüz.

COLUMN Org_Sema FORMAT A12
SELECT LPAD(last_name, LENGTH(last_name)+(LEVEL*2)-2,' ') 
       AS Org_Sema
FROM   employees 
WHERE last_name != 'Kochhar'
START WITH first_name='Steven' AND last_name='King' 
CONNECT BY PRIOR employee_id=manager_id;
ORG_SEMA                  
------------------
King                      
    Greenberg             
      Faviet 
...
106 rows selected.

Ama eğer aşağıdaki gibi değiştirirseniz Kochhar ve buna bağlı olan tüm çalışanlar (Kochhar'ın altındaki çalışanlar/Kochhar'ın çocuk düğümleri) kaldırılır.

COLUMN Org_Sema FORMAT A12
SELECT LPAD(last_name, LENGTH(last_name)+(LEVEL*2)-2,' ') 
       AS Org_Sema
FROM   employees 
START WITH first_name='Steven' AND last_name='King' 
CONNECT BY PRIOR employee_id=manager_id
AND last_name != 'Kochhar';
ORG_SEMA                  
----------------
King                      
  De Haan                 
    Hunold  
...
95 rows selected.

Konuyu burada tamamlıyoruz. Sağlıcakla kalın...

Hiç yorum yok:

Yorum Gönder