Ana içeriğe geç
Kesit SQL
Orta · T-SQL (SQL Server)

PIVOT ve UNPIVOT

SQL Server'ın satırları sütuna çeviren operatörleri.

Uygulamada aç, 5 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

PIVOT operatörü

Koşullu toplama ile pivot yapmayı “İleri Sorgu Yapıları” bölümünde gördük. SQL Server ve Oracle bunun için ayrı bir PIVOT operatörü sunar. Yapı: kaynak sorgu → toplanacak değer → sütuna dönüşecek değerler.

SELECT *
FROM (
    SELECT channel, YEAR(order_date) AS yil, order_id
    FROM orders
) AS kaynak
PIVOT (
    COUNT(order_id) FOR yil IN ([2022], [2023], [2024])
) AS p
ORDER BY channel;
PIVOT tuzağıKaynak sorgudaki, PIVOT'ta kullanılmayan her sütun gruplama sütunu olur. Kaynağa fazladan bir sütun (ör. customer_id) eklersen sonuç satır sayısı patlar. Kaynak sorguda yalnızca gerekli sütunları seç.

Sütun değerleri sabit yazılır. Değer listesi değişkense (ör. her yıl yeni sütun) dinamik SQL ile sorgu metni üretilir; bunu ilerideki derste göreceğiz.

UNPIVOT: sütunları satıra çevirmek

WITH genis AS (
    SELECT channel,
           SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) AS teslim,
           SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS iptal
    FROM orders
    GROUP BY channel
)
SELECT channel, durum, adet
FROM genis
UNPIVOT (adet FOR durum IN (teslim, iptal)) AS u
ORDER BY channel, durum;

Derinleş: dinamik PIVOT ve NULL hücreler

PIVOT'un en büyük kısıtı, sütun listesinin sorgu yazılırken sabit olmasıdır. Lezzet Kurye'ye yarın yeni bir ödeme yöntemi eklenirse IN ([cüzdan], [kart], [nakit]) listesi onu göstermez. Çözüm, sütun listesini veriden üretip sorgu metnini dinamik olarak kurmaktır:

DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX);

SELECT @cols = STRING_AGG(QUOTENAME(payment), ', ') WITHIN GROUP (ORDER BY payment)
FROM (SELECT DISTINCT payment FROM lk_orders) AS t;

SET @sql = N'SELECT status, ' + @cols + N'
FROM (SELECT status, payment, order_id FROM lk_orders) AS k
PIVOT (COUNT(order_id) FOR payment IN (' + @cols + N')) AS p
ORDER BY status;';

PRINT @sql;
EXEC sp_executesql @sql;

NULL hücreler

PIVOT içinde COUNT eşleşme yoksa 0 verir, ama SUM, AVG, MAX NULL verir. Raporlarda boş hücre “veri yok” mu, “sıfır” mı karışmasın diye dış SELECT'te ISNULL uygulanır (PIVOT'un içinde uygulanamaz):

SELECT cuisine,
       ISNULL([İstanbul], 0) AS [İstanbul],
       ISNULL([Ankara], 0)   AS Ankara,
       ISNULL([İzmir], 0)    AS [İzmir]
FROM (SELECT r.cuisine, r.city, o.tip
      FROM lk_orders o JOIN lk_restaurants r ON r.restaurant_id = o.restaurant_id) AS k
PIVOT (SUM(tip) FOR city IN ([İstanbul], [Ankara], [İzmir])) AS p
ORDER BY cuisine;
İhtiyaçPIVOTKoşullu toplama (CASE)
Tek ölçü, sabit sütunlarUygunUygun
Birden çok ölçü (adet + tutar)Tek PIVOT'ta olmazKolay
Farklı koşullar (tarih aralığı gibi)ZorKolay
TaşınabilirlikSQL Server, OracleHer SQL türü

Kontrol listesi: Kaynak sorguda yalnızca satır anahtarı, pivot sütunu ve ölçü var mı? Sütun listesi sıralı ve QUOTENAME'li mi? Toplam fonksiyonu NULL üretebiliyorsa dışarıda ISNULL var mı? Sonucun satır toplamı, pivotsuz bir GROUP BY toplamıyla aynı mı?

Kaynak sorguyu dar tutYukarıdaki kaynağa o.order_id eklersen her sipariş ayrı bir gruplama anahtarı olur ve sonuç 8 satır yerine binlerce satıra çıkar.
Alıştırmalara geç (5)