4.4. Pencere İşlevleri
Bir pencere işlevi, geçerli satırla bir şekilde ilişkili olan bir dizi tablo satırında bir hesaplama gerçekleştirir. Bu hesaplama türü, toplama işlevleriyle yapılabilecek hesaplamayla karşılaştırılabilir. Ancak, pencere işlevleri, penceresiz toplama çağrıların yaptığı gibi satırların tek bir çıktı satırında gruplandırılmasını sağlamaz. Bunun yerine, satırlar kendi kimliklerini korur. Perde arkasında, pencere işlevi, sorgu sonucunun geçerli tek bir satırından çok daha fazlasına erişebilir.
Her çalışanın (employee) maaşını (salary) kendi bölümündeki (depname) ortalama maaşla nasıl karşılaştıracağını gösteren bir örnek:
SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
Çıktısı:
depname | empno | salary | avg -----------+-------+--------+----------------------- develop | 11 | 5200 | 5020.0000000000000000 develop | 7 | 4200 | 5020.0000000000000000 develop | 9 | 4500 | 5020.0000000000000000 develop | 8 | 6000 | 5020.0000000000000000 develop | 10 | 5200 | 5020.0000000000000000 personnel | 5 | 3500 | 3700.0000000000000000 personnel | 2 | 3900 | 3700.0000000000000000 sales | 3 | 4800 | 4866.6666666666666667 sales | 1 | 5000 | 4866.6666666666666667 sales | 4 | 4800 | 4866.6666666666666667 (10 satır)
Çıktının ilk üç sütunu doğrudan empsalary
tablosundan gelir ve tablodaki her satır için bir çıktı satırı vardır. Dördüncü sütun, geçerli satırla aynı depname
değerine sahip tüm satırlardan elde edilen ortalamayı temsil eder. (Bu aslında, pencere olmayan avg
toplamı ile aynı işlevdir, ancak OVER
deyimi, bunun bir pencere işlevi olarak ele alınmasına ve pencere çerçevesi boyunca hesaplanmasına neden olur.)
Bir pencere işlevi çağrısı her zaman pencere işlevinin adını ve bağımsız değişkenlerini izleyen bir OVER
deyimi içerir. Bu, onu normal bir işlevden veya pencere olmayan bir toplamdan sözdizimsel olarak ayıran şeydir. OVER
deyimi, sorgu satırlarının pencere işlevi tarafından işlenmek üzere tam olarak nasıl bölüneceğini belirler. OVER
içindeki PARTITION BY
deyimi, satırları, PARTITION BY
ifade(ler)iyle aynı değerlerini paylaşan gruplara veya bölümlere ayırır. Her satır için pencere işlevi, geçerli satırla aynı bölüme düşen satırlar arasında hesaplanır.
OVER
içinde ORDER BY
kullanarak satırların pencere işlevleri tarafından işlenme sırasını da denetim altına alabilirsiniz. (ORDER BY
penceresinin çıktı satırlarının sırasına uyması bile gerekmez.) İşte bir örnek:
SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary;
Çıktısı:
depname | empno | salary | rank -----------+-------+--------+------ develop | 8 | 6000 | 1 develop | 10 | 5200 | 2 develop | 11 | 5200 | 2 develop | 9 | 4500 | 4 develop | 7 | 4200 | 5 personnel | 2 | 3900 | 1 personnel | 5 | 3500 | 2 sales | 1 | 5000 | 1 sales | 4 | 4800 | 2 sales | 3 | 4800 | 2 (10 satır)
Burada gösterildiği gibi, rank
() işlevi, ORDER BY
deyimi tarafından tanımlanan sırayı kullanarak, geçerli satırın bölümündeki her farklı ORDER BY
değeri için sayısal bir sıra üretir. rank
() işlevinin bağımsız değişkenleri yoktur, çünkü davranışı tamamen OVER
deyimi tarafından belirlenir.
Bir pencere işlevi tarafından dikkate alınan satırlar, sorgunun FROM
deyimi tarafından WHERE
, GROUP BY
ve varsa HAVING
deyimleri ile elde edilen "hayali" bir tablonun satırlarıdır. Örneğin, WHERE
koşulunu karşılamadığı için kaldırılan bir satırı herhangi bir pencere işlevi göremez. Bir sorgu, farklı OVER
deyimleri kullanarak verileri farklı şekillerde dilimleyen birden çok pencere işlevi içerebilir, ancak bunların tümü bu "hayali" tablo tarafından tanımlanan aynı satır kümesinde hareket eder.
Satırların sıralaması önemli değilse, ORDER BY
deyiminin atlanabileceğini zaten gördük. PARTITION
BY
deyimini de atlamak da mümkündür, bu durumda tüm satırları içeren tek bir bölüm olur.
Pencere işlevleriyle ilişkili önemli kavram daha vardır: her satır için, kendi bölümünde pencere çerçevesi adı verilen bir dizi satır vardır. Bazı pencere işlevleri, tüm bölme yerine yalnızca pencere çerçevesinin satırları üzerinde hareket eder. Öntanımlı olarak, ORDER BY
belirtilirse, pencere çerçevesi bölümün başlangıcından geçerli satıra kadar olan tüm satırlardan ve takip eden satırlardan ORDER BY
deyimine göre geçerli satırla eşleşen satırlardan oluşur. ORDER BY
atlandığında, öntanımlı pencere çerçevesi bölümdeki tüm satırlardan oluşur.[67]
İşte sum
() işlevini kullanan bir örnek:
SELECT salary, sum(salary) OVER () FROM empsalary;
Çıktısı:
salary | sum --------+------- 5200 | 47100 5000 | 47100 3500 | 47100 4800 | 47100 3900 | 47100 4200 | 47100 4500 | 47100 4800 | 47100 6000 | 47100 5200 | 47100 (10 satır)
Yukarıda, OVER
deyiminde ORDER BY
olmadığı için pencere çerçevesi, PARTITION BY
olmadığı için tablonun tamamından oluşan bölme ile aynıdır; diğer bir deyişle, her toplam tablonun tamamından elde edilir ve böylece her çıktı satırı için aynı sonucu elde ederiz. Ancak bir ORDER BY
deyimi eklersek çok farklı sonuçlar elde ederiz:
SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
Çıktısı:
salary | sum --------+------- 3500 | 3500 3900 | 7400 4200 | 11600 4500 | 16100 4800 | 25700 4800 | 25700 5000 | 30700 5200 | 41100 5200 | 41100 6000 | 47100 (10 satır)
Maaşlar düşükten yükseğe sıralanırken, ikinci sütunda her maaş kendinden öncekilerin toplamına eklenerek gösterilir.
Pencere işlevlerine yalnızca SELECT
listesinde ve sorgunun ORDER BY deyiminde izin verilir. GROUP BY
, HAVING
ve WHERE
deyimleri gibi başka yerlerde yasaktır. Bunun nedeni, bu deyimlerin işlenmesinden sonra mantıksal olarak yürütülmeleridir. Ayrıca, pencere işlevleri, penceresiz toplu işlevlerden sonra yürütülür. Bu, bir pencere işlevinin bağımsız değişkenlerine bir toplu işlev çağrısını dahil etmenin geçerli olduğu, ancak bunun tersinin geçerli olmadığı anlamına gelir.
Pencere hesaplamaları yapıldıktan sonra satırları elemek veya gruplandırmak gerekirse, bir alt seçim kullanabilirsiniz. Örneğin:
SELECT depname, empno, salary, enroll_date FROM (SELECT depname, empno, salary, enroll_date, rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos FROM empsalary ) AS ss WHERE pos < 3;
Yukarıdaki sorgu, iç sorguda sıralanan satırlardan ilk 3'ünü gösterir.
Bir sorgu birden çok pencere işlevi içerdiğinde, her biri için ayrı bir OVER
deyimi yazmak mümkündür, ancak birkaç işlev için aynı pencereleme davranışı istenirse bu tekrarlanabilir ama hataya açıktır. Bunun yerine, her pencereleme davranışı bir WINDOW
deyiminde adlandırılıp OVER
deyiminde bu ada başvurulabilir. Örnek:
SELECT sum(salary) OVER w, avg(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);
Pencere işlevleri hakkında daha ayrıntılı bilgi PostgreSQL belgelerindeki Window Function Calls, Window Functions, Window Function Processing bölümlerinde ve SELECT kılavuz sayfasında bulunabilir.
[67] Pencere çerçevesini başka şekillerde tanımlamak da mümkündür, ancak bu eğitim bunları kapsamamaktadır. Ayrıntılar Window Function Calls bölümünde bulunabilir.