Author Archive

SQL’de Temp Table ve Table Variable

SQL geliştirme ve optimizasyon süreçlerinde, geçici veri depolama ihtiyacı sıkça ortaya çıkar. Bu ihtiyaç, karmaşık sorguların ara sonuçlarını saklamak, veri manipülasyonları yapmak veya kod modülerliğini artırmak için hayati öneme sahiptir. SQL Server’da bu amaçla yaygın olarak kullanılan iki temel mekanizma “Temp Table” (Geçici Tablo) ve “Table Variable” (Tablo Değişkeni) kavramlarıdır. Her ikisi de geçici veri barındırsa da, iç işleyişleri, kullanım alanları ve performans karakteristikleri açısından belirgin farklılıklar gösterirler. Bu makale, her iki yapıyı da detaylı bir şekilde inceleyerek, hangi senaryoda hangisinin tercih edilmesi gerektiğini açıklayacaktır.

Geçici Tablolar (Temp Tables)

Geçici tablolar, adı üzerinde, sadece belirli bir süre boyunca var olan ve genellikle tempdb veritabanında saklanan gerçek tablolardır. SQL Server, geçici tabloları normal tablolar gibi yönetir; bu da onların indekslere, kısıtlamalara (PRIMARY KEY, UNIQUE gibi) ve istatistiklere sahip olabileceği anlamına gelir.

Türleri ve Yaşam Döngüsü

Geçici tablolar iki ana türe ayrılır:

  • Yerel Geçici Tablolar (#TableName): Tek bir kullanıcı oturumuyla sınırlıdır. Tabloyu oluşturan oturum sona erdiğinde veya açıkça DROP TABLE #TableName komutuyla bırakıldığında otomatik olarak silinirler. Farklı oturumlar aynı isimde yerel geçici tablolar oluşturabilir ve bunlar birbirlerinden bağımsızdır.
  • Genel Geçici Tablolar (##TableName): Tüm kullanıcı oturumları tarafından erişilebilirler. Tabloyu oluşturan oturum sona erdiğinde ve diğer tüm oturumlar bu tabloya referans vermeyi bıraktığında otomatik olarak silinirler. Adları sunucu genelinde benzersiz olmalıdır.

Özellikleri ve Kullanım Senaryoları

  • tempdb Üzerinde Depolama: Geçici tablolar fiziksel olarak tempdb veritabanında saklanır. Bu, diske I/O işlemlerine neden olabilir.
  • İndeksler ve İstatistikler: Geçici tablolara, tıpkı kalıcı tablolarda olduğu gibi kümelenmiş (clustered) ve kümelenmemiş (non-clustered) indeksler eklenebilir. SQL Server sorgu iyileştiricisi (optimizer), bu indeksleri ve otomatik olarak oluşturulan istatistikleri kullanarak sorgu planlarını optimize eder. Bu özellik, büyük veri kümeleriyle çalışırken veya karmaşık birleştirme (JOIN) ve sıralama (ORDER BY) işlemleri gerektiren sorgularda performansı önemli ölçüde artırır.
  • İşlem (Transaction) Desteği: Geçici tablolar üzerindeki işlemler geri alınabilir (rollback edilebilir). Bu, veri bütünlüğü açısından kritik durumlarda avantaj sağlar.
  • Saklı Yordamlar Arası Erişim: Yerel geçici tablolar, onları oluşturan saklı yordam tarafından oluşturulduktan sonra, bu yordamın çağırdığı diğer saklı yordamlardan da erişilebilir (ancak çağıran yordamın scope’u içinde kalır).
  • Büyük Veri Kümeleri: Çok sayıda satır işlenecekse veya bu veriler üzerinde karmaşık operasyonlar (JOIN, GROUP BY, vs.) yapılacaksa geçici tablolar daha iyi bir seçenektir.

Örnek Kullanım

-- Yerel Geçici Tablo Oluşturma
CREATE TABLE #PersonelGecici (
    ID INT PRIMARY KEY,
    Ad NVARCHAR(50),
    Soyad NVARCHAR(50),
    DepartmanID INT
);

-- Veri Ekleme
INSERT INTO #PersonelGecici (ID, Ad, Soyad, DepartmanID)
VALUES
    (1, 'Ali', 'Can', 101),
    (2, 'Ayşe', 'Demir', 102),
    (3, 'Mehmet', 'Yılmaz', 101);

-- Sorgulama
SELECT * FROM #PersonelGecici WHERE DepartmanID = 101;

-- İndeks Ekleme (örnek)
CREATE INDEX IX_DepartmanID ON #PersonelGecici (DepartmanID);

-- Tabloyu Silme (oturum sonlandığında otomatik silinir, ancak manuel de silinebilir)
DROP TABLE #PersonelGecici;

Tablo Değişkenleri (Table Variables)

Tablo değişkenleri, bir SQL değişkeni gibi bildirilip kullanılan, ancak TABLE veri tipinde olan özel yapılardır. Genellikle bellekte depolandığı düşünülse de, belirli durumlarda (boyutları büyüdüğünde veya karmaşık işlemler gerektirdiğinde) tempdb‘ye taşınabilirler. Ancak, tempdb‘de depolansalar bile, geçici tablolardan farklı bir mekanizma ile yönetilirler ve daha hafif bir yaklaşıma sahiptirler.

Yaşam Döngüsü

Tablo değişkenlerinin yaşam döngüsü, bildirildiği T-SQL toplu işi (batch), saklı yordam veya fonksiyonla sınırlıdır. Bu kapsamın dışına çıkıldığında veya toplu iş sona erdiğinde, tablo değişkeni otomatik olarak kapsamdan çıkarılır ve bellekten silinir. İşlem (transaction) dışında davranırlar, yani üzerlerindeki değişiklikler geri alınamaz (rollback).

Özellikleri ve Kullanım Senaryoları

  • Daha Hafif Kaynak Kullanımı: Geçici tablolara kıyasla daha az kilitlenme (locking) ve günlük kaydı (logging) yükü oluştururlar. Bu, özellikle küçük veri kümeleri üzerinde sık yapılan işlemler için avantajlıdır.
  • İstatistik Yokluğu: Tablo değişkenleri için SQL Server istatistik tutmaz. Sorgu iyileştiricisi genellikle tablo değişkeninde yalnızca bir satır olduğunu varsayar. Bu durum, tablo değişkeni büyük miktarda veri içeriyorsa suboptimal (ideal olmayan) sorgu planlarına yol açabilir.
  • Sadece Kümelenmiş İndeks (PRIMARY KEY/UNIQUE CONSTRAINT): Tablo değişkenleri üzerinde yalnızca PRIMARY KEY veya UNIQUE CONSTRAINT tanımlanabilir, bu da örtük olarak bir kümelenmiş indeks oluşturur. Kümelenmemiş (non-clustered) indeksler oluşturulamaz.
  • Saklı Yordam Yeniden Derlemesi Etkisi Yok: Bir saklı yordam içinde tablo değişkeni kullanılması, yordamın her çalıştırıldığında yeniden derlenmesine neden olmaz. Geçici tablolar ise bazen saklı yordamların yeniden derlenmesine yol açabilir.
  • Küçük Veri Kümeleri: Genellikle birkaç yüz veya bin satır gibi nispeten küçük veri kümelerini işlemek için uygundurlar.

Örnek Kullanım

-- Tablo Değişkeni Bildirme
DECLARE @Urunler TABLE (
    UrunID INT PRIMARY KEY,
    UrunAdi NVARCHAR(100),
    Fiyat DECIMAL(10, 2)
);

-- Veri Ekleme
INSERT INTO @Urunler (UrunID, UrunAdi, Fiyat)
VALUES
    (101, 'Klavye', 150.00),
    (102, 'Fare', 75.50),
    (103, 'Monitör', 1200.00);

-- Sorgulama
SELECT * FROM @Urunler WHERE Fiyat > 100.00;

-- Tablo değişkeni, betik (batch) sona erdiğinde otomatik olarak kapsamdan çıkar.

Temp Table vs. Table Variable: Doğru Aracı Seçmek

Hem geçici tablolar hem de tablo değişkenleri, SQL geliştiricilerinin araç kutusundaki değerli enstrümanlardır. Ancak, her birinin kendine özgü güçlü ve zayıf yönleri olduğundan, kullanım senaryosuna göre doğru aracı seçmek performans ve kaynak yönetimi açısından kritik öneme sahiptir.

  • Veri Boyutu ve Karmaşıklığı: Eğer işlenecek veri kümesi büyükse (binlerce veya milyonlarca satır) ve üzerinde karmaşık birleştirmeler, filtrelemeler veya sıralamalar yapılacaksa, geçici tablolar genellikle daha iyi bir seçimdir. Çünkü indeksleme ve istatistik mekanizmaları sayesinde sorgu iyileştiricisi daha verimli planlar oluşturabilir. Küçük ve basit veri kümeleri için ise tablo değişkenleri daha az overhead ile hızlı çalışabilir.
  • İndeksleme İhtiyacı: Sorgularınızın performansı için kümelenmemiş (non-clustered) indekslere ihtiyaç duyuyorsanız, tek seçeneğiniz geçici tablolardır. Tablo değişkenleri sadece birincil anahtar veya benzersiz kısıtlamalar üzerinden kümelenmiş indeks benzeri bir yapıya izin verir.
  • İstatistikler ve Sorgu Planları: SQL Server sorgu iyileştiricisi, geçici tablolar için istatistik tutar ve bunlara dayalı olarak optimum sorgu planları oluşturur. Tablo değişkenleri için istatistik tutulmadığı ve genellikle tek satır varsayımı yapıldığı için, büyük veri kümelerinde geçici tablolar daha performanslı sorgu planları üretebilir.
  • İşlem Desteği: Eğer geçici verileriniz üzerindeki değişikliklerin işlem (transaction) kapsamında geri alınabilmesi gerekiyorsa, geçici tabloları tercih etmelisiniz. Tablo değişkenleri işlem kapsamında değildir.
  • Saklı Yordam Yeniden Derleme (Recompilation): Çok sık çalıştırılan ve performans kritik saklı yordamlar içinde tablo değişkenleri kullanmak, yordamın yeniden derlenmesini engellediği için daha avantajlı olabilir. Geçici tablolar, şema değişiklikleri nedeniyle bazen yeniden derlemeye yol açabilir.
  • tempdb Çekişmesi (Contention): Çok sayıda eş zamanlı işlem tempdb üzerinde yoğun I/O çekişmesine neden oluyorsa, küçük veri kümeleri için tablo değişkenleri daha hafif bir alternatif sunabilir.

Özetle, SQL’deki geçici tablolar ve tablo değişkenleri, ara veri yönetimi için vazgeçilmez araçlardır. Geçici tablolar, büyük, karmaşık, indeksleme ve işlem desteği gerektiren senaryolar için güçlü bir çözüm sunarken, tablo değişkenleri küçük, basit ve yeniden derleme kaygısının olduğu durumlar için daha hafif ve çevik bir alternatiftir. Doğru seçimi yapmak, veri hacmini, indeksleme ihtiyaçlarını, sorgu optimizasyon beklentilerini ve işlem gereksinimlerini dikkatlice değerlendirerek mümkündür. Her iki yapının da potansiyelini anlamak, daha verimli ve optimize edilmiş SQL kodları yazmanın anahtarıdır.


SQL’de Dynamic SQL

SQL geliştiricilerinin güçlü araçlarından biri olan Dinamik SQL, çalışma zamanında SQL sorguları oluşturma ve yürütme yeteneği sunar. Bu esneklik, statik SQL’in yetersiz kaldığı karmaşık senaryolarda, örneğin değişken tablo adları, dinamik filtreleme veya bilinmeyen sütun yapılarıyla çalışırken kritik bir rol oynar. Doğru kullanıldığında verimliliği artırırken, yanlış yönetildiğinde ciddi güvenlik açıkları ve performans sorunları yaratabilir.

Dinamik SQL Nedir?

Dinamik SQL, bir SQL sorgusunun veya komutunun, uygulamanın çalışması sırasında programlı olarak oluşturulması ve yürütülmesidir. Geleneksel (statik) SQL sorguları, genellikle derleme zamanında veya uygulama dağıtılırken tamamen tanımlanmışken, Dinamik SQL, sorgu metnini değişkenler, kullanıcı girişleri veya diğer çalışma zamanı koşullarına göre dinamik olarak oluşturur. Bu, sorgunun içeriğinin veya hedeflediği veritabanı nesnelerinin (tablolar, sütunlar vb.) çalışma zamanında belirlenmesine olanak tanır. Genellikle, bir SQL komut dizisi bir dize (string) olarak oluşturulur ve ardından veritabanı motoruna yürütülmesi için gönderilir.

Neden Dinamik SQL Kullanılır?

Dinamik SQL, belirli durumlarda vazgeçilmez bir çözüm sunar:

  • Esneklik ve Özelleştirilebilirlik: Kullanıcıların arama kriterlerini, sıralama düzenlerini veya görüntülemek istedikleri sütunları dinamik olarak belirlemesine olanak tanıyan raporlama ve arama ekranları oluşturmak için idealdir. Örneğin, belirli filtrelerin sadece kullanıcı seçimi durumunda WHERE cümlesine eklenmesi.
  • Bilinmeyen Veritabanı Yapıları: Çalışma zamanında tablo veya sütun adları gibi veritabanı nesnelerinin adlarını bilmediğiniz senaryolarda (örneğin, çok kiracılı sistemlerde her kiracı için ayrı tablolar) kullanılır.
  • Genel Amaçlı Fonksiyonlar: Veritabanı şemasından bağımsız olarak çalışabilen, genel veri erişim katmanları veya yardımcı programlar oluşturmak için kullanılabilir.
  • Karmaşık Yönetim Görevleri: Veritabanı bakımı veya şema değişiklikleri gibi görevleri otomatikleştirmek için (örneğin, belirli bir desene uyan tüm tablolara bir dizin eklemek).

Dinamik SQL Nasıl Uygulanır?

SQL Server’da Dinamik SQL genellikle iki ana yöntemle uygulanır: EXECUTE/EXEC komutu ve sp_executesql saklı yordamı.

EXECUTE / EXEC Komutu

EXECUTE veya kısaca EXEC, en basit Dinamik SQL yürütme yöntemidir. Bir string değişkenin veya değişmez dizenin içeriğini SQL komutu olarak yorumlayıp çalıştırır.

DECLARE @sqlCommand NVARCHAR(MAX);
DECLARE @tableName NVARCHAR(50) = 'Customers';

SET @sqlCommand = 'SELECT TOP 5 * FROM ' + @tableName + ' WHERE Country = ''USA''';

EXEC (@sqlCommand);

Bu yöntem hızlı ve kolaydır ancak ciddi dezavantajları vardır:

  • SQL Injection Riski: Kullanıcı girişleri doğrudan sorgu dizisine eklenirse, SQL Injection saldırılarına açıktır.
  • Performans Sorunları: Her yürütmede sorguyu yeniden derlemeye eğilimlidir, bu da plan önbelleğini verimsiz hale getirir ve performansı düşürür. Parametre kullanamaz.
  • Sınırlı Hata Ayıklama: Derleme zamanında hataları tespit etmek daha zordur.

sp_executesql Saklı Yordamı

sp_executesql, Dinamik SQL’i güvenli ve performanslı bir şekilde yürütmek için tercih edilen yöntemdir. Parametreleri destekler, bu da SQL Injection’ı önlemeye ve sorgu planının önbelleğe alınmasına yardımcı olur.

DECLARE @sqlCommand NVARCHAR(MAX);
DECLARE @params NVARCHAR(MAX);
DECLARE @tableName NVARCHAR(50) = 'Orders';
DECLARE @orderStatus NVARCHAR(20) = 'Shipped';
DECLARE @totalOrders INT;

SET @sqlCommand = N'SELECT @orderCount = COUNT(*) FROM ' + QUOTENAME(@tableName) + N' WHERE Status = @statusParam';
SET @params = N'@statusParam NVARCHAR(20), @orderCount INT OUTPUT';

EXEC sp_executesql 
    @sqlCommand, 
    @params, 
    @statusParam = @orderStatus,
    @orderCount = @totalOrders OUTPUT;

SELECT @totalOrders AS ShippedOrdersCount;

sp_executesql‘in avantajları:

  • SQL Injection Koruması: Parametreler, sorgu metninden ayrı olarak geçirildiği için kullanıcı girişlerinin SQL komutları olarak yorumlanmasını engeller.
  • Gelişmiş Performans: Parametreli sorgular aynı sorgu planını tekrar tekrar kullanabilir, bu da derleme maliyetini azaltır ve önbellek kullanımını optimize eder.
  • Daha İyi Hata Yönetimi: Hata mesajları genellikle daha bilgilendiricidir.
  • Çıktı Parametreleri: Saklı yordamlar gibi çıktı parametrelerini destekler.

Güvenlik Endişeleri: SQL Injection

Dinamik SQL’in en büyük güvenlik riski SQL Injection’dır. Kötü niyetli bir kullanıcı, sorgu dizisine eklenen kullanıcı girişi aracılığıyla zararlı SQL kodları enjekte edebilir. Örneğin, EXEC ('SELECT * FROM Users WHERE Username = ''' + @userInput + '''') ifadesinde, @userInput değişkenine ' OR '1'='1 gibi bir değer girildiğinde, sorgu tüm kullanıcıları döndürebilir veya '; DROP TABLE Users; -- gibi komutlarla veri kaybına yol açabilir.

Bu tür saldırıları önlemek için:

  • Her zaman sp_executesql kullanın ve tüm kullanıcı girişlerini parametre olarak geçirin.
  • Dinamik olarak oluşturulan nesne adlarını (tablo, sütun) QUOTENAME() fonksiyonu ile tırnak içine alın.
  • Gelen tüm kullanıcı girişlerini ve parametreleri sunucu tarafında doğrulamayı ve temizlemeyi unutmayın.

Performans Mülahazaları

Dinamik SQL kullanırken performans, özellikle büyük ölçekli uygulamalarda kritik bir faktördür:

  • Derleme Maliyeti: Her Dinamik SQL sorgusu, ilk kez yürütüldüğünde derlenmelidir. sp_executesql ile parametre kullanımı, SQL Server’ın aynı sorgu için mevcut bir planı önbellekten yeniden kullanmasına olanak tanıyarak bu maliyeti azaltır. EXEC ile her zaman yeni bir derleme gerçekleşebilir.
  • Plan Önbelleği: sp_executesql, parametreleştirme sayesinde plan önbelleğini daha verimli kullanır. Bu, statik SQL’e yakın performans sergilemesini sağlar. EXEC ise her seferinde yeni bir sorgu olarak algılanabilir, bu da önbelleği doldurur ve önbellekteki diğer faydalı planları dışarı atabilir.
  • Aşırı Kullanım: Basit, statik sorgular için Dinamik SQL kullanmaktan kaçının. Bu, gereksiz karmaşıklık ve performans yükü yaratır.

En İyi Uygulamalar ve Gelişmiş Kullanımlar

  • Her Zaman sp_executesql Kullanın: Güvenlik ve performans nedeniyle tercih edilmesi gereken yöntemdir.
  • Tanımlayıcıları Tırnak İçine Alın: Dinamik olarak bir tablo veya sütun adı ekliyorsanız, QUOTENAME() fonksiyonunu kullanarak olası enjeksiyon saldırılarını önleyin ve boşluk içeren adlarla çalışmayı sağlayın. Örneğin, N'SELECT * FROM ' + QUOTENAME(@tableName).
  • Giriş Doğrulama: Dinamik SQL sorgularına giden tüm girişleri (hem kullanıcıdan gelen hem de uygulama içi değişkenler) kapsamlı bir şekilde doğrulayın.
  • Minimum Kullanım: Dinamik SQL’i yalnızca gerçekten ihtiyaç duyulduğunda kullanın. Alternatif olarak statik SQL, indeksleme veya görünüm (view) kullanılabiliyorsa bunları tercih edin.
  • Hata Yönetimi: Dinamik sorgularınızda olası hataları yakalamak için TRY...CATCH bloklarını kullanın.
  • Karmaşıklığı Yönetin: Çok karmaşık Dinamik SQL oluşturmaktan kaçının. Gerekirse, daha küçük, yönetilebilir parçalara ayırın veya saklı yordamlar içinde Dinamik SQL kullanın.

Dinamik SQL, esneklik ve özelleştirilebilirlik sağlayan, güçlü bir SQL özelliğidir. Ancak bu gücün beraberinde ciddi güvenlik riskleri ve performans sorunları getirebileceği unutulmamalıdır. SQL Injection tehdidini önlemek ve performansı optimize etmek için her zaman sp_executesql kullanmalı, parametreleştirmeye özen göstermelisiniz. Doğru uygulandığında, karmaşık gereksinimleri karşılamak için vazgeçilmez bir araç haline gelirken, dikkatsiz kullanım potansiyel tehlikeler barındırır.


SQL’de Pivot ve Unpivot İşlemleri

SQL veritabanlarında veri analizi ve raporlama süreçlerinde karşılaşılan en yaygın zorluklardan biri, verilerin sunum formatını değiştirmektir. Pivot ve Unpivot işlemleri, bu tür veri dönüşümlerini kolaylaştıran güçlü araçlardır. Pivot, dikey satırları yatay sütunlara dönüştürerek özet tablolar oluştururken, Unpivot ise yatay sütunları dikey satırlara çevirerek normalleştirilmiş bir görünüm sağlar. Bu işlemler, veri analistlerinin ve geliştiricilerin veriyi daha anlamlı hale getirmesine olanak tanır.

SQL’de Pivot İşlemi

Pivot işlemi, SQL’de satırlardaki benzersiz değerleri sütun başlıkları olarak yeniden düzenlemenizi sağlayan güçlü bir veri dönüştürme aracıdır. Genellikle veri özetleri, çapraz tablolar veya belirli bir kategoriye göre gruplandırılmış toplamlar oluşturmak için kullanılır. Temel amacı, dikey bir veri kümesini daha yatay ve okunabilir bir formata getirmektir.

Bir Pivot işlemi gerçekleştirdiğinizde, bir veya daha fazla sütunun değerleri yeni sütun başlıkları haline gelir ve bu yeni sütunların değerleri ise genellikle bir toplama (aggregate) fonksiyonu (SUM, COUNT, AVG, MAX, MIN vb.) kullanılarak hesaplanır. Örneğin, farklı aylardaki satış miktarlarını tek bir satırda, her ayın kendi sütunu altında görmek istediğinizde Pivot devreye girer.

Pivot Kullanım Senaryoları

  • Raporlama: Aylık, yıllık veya çeyreklik satış raporlarını, her dönemin kendi sütununda göstererek daha anlaşılır hale getirme.
  • Veri Analizi: Farklı ürün kategorilerinin veya bölgelerin performansını tek bir görünümde karşılaştırma.
  • Görselleştirme Öncesi Hazırlık: Grafik ve çizelge oluşturmak için belirli bir formatta veri gerektiren araçlara veri sağlama.
  • Daha Okunabilir Tablolar: Çok sayıda satırı olan, ancak belirli bir boyuta göre özetlenmesi gereken tabloları basitleştirme.

Pivot Örneği

Aşağıdaki örnekte, farklı bölgelerdeki aylık satış miktarlarını içeren bir tablonun nasıl Pivot işlemiyle bölgelere göre aylık satış toplamlarını gösteren yatay bir tabloya dönüştürüldüğünü görebilirsiniz.

Örnek Veri Seti Oluşturma:

CREATE TABLE Satislar (
    Bolge VARCHAR(50),
    Ay VARCHAR(20),
    SatisMiktari INT
);

INSERT INTO Satislar (Bolge, Ay, SatisMiktari) VALUES
('Kuzey', 'Ocak', 100),
('Kuzey', 'Şubat', 120),
('Güney', 'Ocak', 90),
('Güney', 'Şubat', 110),
('Doğu', 'Ocak', 150),
('Doğu', 'Şubat', 130),
('Kuzey', 'Mart', 105),
('Güney', 'Mart', 95),
('Doğu', 'Mart', 140);

Pivot Sorgusu:

SELECT Bolge, Ocak, Şubat, Mart
FROM (
    SELECT Bolge, Ay, SatisMiktari
    FROM Satislar
) AS SourceTable
PIVOT
(
    SUM(SatisMiktari) -- Toplama fonksiyonu
    FOR Ay IN ([Ocak], [Şubat], [Mart]) -- Sütunlara dönüştürülecek değerler
) AS PivotTable;

Bu sorgunun sonucu aşağıdaki gibi olacaktır:

Bolge | Ocak | Şubat | Mart
------|------|-------|-----
Kuzey | 100  | 120   | 105
Güney | 90   | 110   | 95
Doğu  | 150  | 130   | 140

Gördüğünüz gibi, her bölge için Ocak, Şubat ve Mart aylarına ait satış miktarları ayrı sütunlarda özetlenmiştir.

SQL’de Unpivot İşlemi

Unpivot işlemi, Pivot işleminin tam tersidir. Yani, yatay (geniş) bir tablodaki sütunları dikey (dar) satırlara dönüştürmek için kullanılır. Bu, özellikle verilerin normalleştirilmesi gerektiğinde veya farklı kaynaklardan gelen, anormalleştirilmiş (örneğin, her ay için ayrı bir sütun içeren) verileri standart bir yapıya getirmek istediğinizde kullanışlıdır.

Unpivot, genellikle bir tabloyu “anahtar-değer” çiftleri formatına dönüştürür. Mevcut sütun isimleri, yeni bir “anahtar” sütunun değerleri haline gelirken, bu sütunlardaki orijinal değerler de yeni bir “değer” sütununa taşınır. Bu işlem, veriyi daha esnek bir şekilde sorgulamak ve analiz etmek için temel bir adımdır.

Unpivot Kullanım Senaryoları

  • Veri Normalleştirme: Yatay olarak genişlemiş tabloları (örn. her yıl veya ay için ayrı sütunlar) daha standart, dikey bir formata dönüştürme.
  • Veri Ambarı ve ETL Süreçleri: Kaynak sistemlerden gelen geniş tabloları, veri ambarı şemasına uygun dar tablolara dönüştürme.
  • Raporlama ve Analiz Esnekliği: Belirli bir ölçütün (örn. ay) dinamik olarak sorgulanabilmesi için veriyi yeniden yapılandırma.
  • Uygulama Entegrasyonu: Bazı uygulamalar veya API’ler, verileri anahtar-değer çiftleri şeklinde talep ettiğinde Unpivot oldukça faydalıdır.

Unpivot Örneği

Aşağıdaki örnekte, Pivot işlemiyle oluşturduğumuz veya benzer yapıda olan aylık satış raporunun (yatay format) Unpivot işlemiyle tekrar dikey formata nasıl dönüştürüldüğünü görebilirsiniz.

Örnek Veri Seti Oluşturma (Pivot edilmiş gibi düşünülen):

CREATE TABLE AylikSatisRaporu (
    Bolge VARCHAR(50),
    Ocak INT,
    Şubat INT,
    Mart INT
);

INSERT INTO AylikSatisRaporu (Bolge, Ocak, Şubat, Mart) VALUES
('Kuzey', 100, 120, 105),
('Güney', 90, 110, 95),
('Doğu', 150, 130, 140);

Unpivot Sorgusu:

SELECT Bolge, Ay, SatisMiktari
FROM AylikSatisRaporu
UNPIVOT
(
    SatisMiktari FOR Ay IN (Ocak, Şubat, Mart) -- SatisMiktari yeni değer sütunu, Ay yeni anahtar sütunu
) AS UnpivotTable;

Bu sorgunun sonucu aşağıdaki gibi olacaktır:

Bolge | Ay    | SatisMiktari
------|-------|-------------
Kuzey | Ocak  | 100
Kuzey | Şubat | 120
Kuzey | Mart  | 105
Güney | Ocak  | 90
Güney | Şubat | 110
Güney | Mart  | 95
Doğu  | Ocak  | 150
Doğu  | Şubat | 130
Doğu  | Mart  | 140

Gördüğünüz gibi, Ocak, Şubat ve Mart sütunları, Ay adında tek bir sütuna dönüştürülmüş ve ilgili satış miktarları da SatisMiktari sütununda yer almıştır.

Pivot ve Unpivot Arasındaki Farklar ve Kullanım Amaçları

Pivot ve Unpivot, veri dönüşümü konusunda birbirini tamamlayan ancak zıt yönlerde çalışan iki güçlü SQL fonksiyonudur. Aralarındaki temel fark, veriyi hangi yönde dönüştürdükleridir:

  • Pivot: “Dikeyden Yataya” dönüşüm yapar. Satırlardaki benzersiz değerleri sütun başlıklarına taşır ve genellikle toplama fonksiyonlarıyla birleştirilerek özetlenmiş, çapraz tablolar oluşturur. Temel amacı, veriyi daha özetlenmiş ve raporlamaya uygun hale getirmektir.
  • Unpivot: “Yataydan Dikeye” dönüşüm yapar. Geniş bir tablonun birden fazla sütununu, yeni bir “anahtar” sütunu ve bir “değer” sütunu oluşturarak dikey satırlara dönüştürür. Temel amacı, anormalleştirilmiş veriyi normalleştirmek, veri ambarı yüklemelerine hazırlamak veya daha esnek sorgulamalar için veri yapısını basitleştirmektir.

Her iki işlem de veri analistleri ve geliştiriciler için kritik öneme sahiptir. Pivot, karmaşık veri kümelerinden hızlı ve anlaşılır özetler çıkarmak için idealdir. Unpivot ise, farklı formatlardaki verileri standart bir yapıya getirme ve büyük veri kümelerinde toplu işlemler yapma konusunda büyük kolaylık sağlar. Bu iki fonksiyonu anlamak ve doğru senaryolarda kullanmak, veri manipülasyonu yeteneğinizi önemli ölçüde artıracaktır.

SQL’deki Pivot ve Unpivot işlemleri, veri dönüşümü ve raporlama ihtiyaçları için vazgeçilmez araçlardır. Pivot, veriyi daha okunabilir ve özetlenmiş bir formatta sunarak analitik süreçleri kolaylaştırırken, Unpivot ise normalleştirme ve toplu veri işleme için ideal bir çözüm sunar. Bu fonksiyonları etkin bir şekilde kullanmak, SQL geliştiricileri ve veri analistleri için veri manipülasyonu becerilerini önemli ölçüde geliştirecek ve daha esnek raporlama imkanları sağlayacaktır.


SQL’de Window Fonksiyonları

SQL veritabanı yönetim sistemlerinde veri analizi yaparken karşılaşılan birçok karmaşık senaryo, geleneksel GROUP BY yapısıyla çözülememektedir. İşte tam bu noktada SQL Pencere Fonksiyonları (Window Functions) devreye girer. Bu güçlü araçlar, veri setinin tamamını veya belirli bölümlerini analiz ederken her bir satırın kendi bağlamında değerlendirilmesine olanak tanır. Özellikle detaylı raporlama ve ileri düzey analizler için vazgeçilmez bir yardımcıdır.

SQL Pencere Fonksiyonları Neden Gereklidir?

Geleneksel SQL sorgularında kullanılan GROUP BY ifadesi, satırları belirli kriterlere göre gruplar ve her grup için tek bir özet değer (örneğin toplam, ortalama) döndürür. Ancak bu işlem, gruplanan satırların detaylarını kaybetmemize neden olur. Örneğin, bir departmandaki her çalışanın maaşını gösterirken aynı zamanda departmanın ortalama maaşını da görmek istediğimizde GROUP BY yetersiz kalır. Çünkü GROUP BY kullandığımızda ya tüm çalışanların listesini ya da sadece departman ortalamasını görebiliriz, ikisini bir arada aynı satırda görüntüleyemeyiz. Pencere Fonksiyonları, bu kısıtlamayı ortadan kaldırarak her bir orijinal satırı korurken, aynı zamanda belirli bir “pencere” (window) içindeki diğer satırlara göre hesaplamalar yapmamızı sağlar.

Pencere Fonksiyonları Nedir ve Nasıl Çalışır?

Pencere fonksiyonları, belirli bir satır kümesi üzerinde işlem yapan ve her bir orijinal satır için bir değer döndüren fonksiyonlardır. Bu satır kümesi, ilgili satırla ilişkili olan ve OVER() yan tümcesi ile tanımlanan bir “pencere”dir. Geleneksel aggregate fonksiyonlarından (SUM, AVG, COUNT vb.) temel farkı, satırları gruplayıp tek bir sonuç satırı döndürmek yerine, her orijinal satır için bir sonuç döndürmesidir.

OVER() Yan Tümcesi

Pencere fonksiyonlarının kalbi OVER() yan tümcesidir. Bu yan tümce, fonksiyonun hangi satırlar üzerinde çalışacağını, yani “pencereyi” nasıl tanımlayacağını belirtir. OVER() kendi başına kullanıldığında, tüm sonuç kümesini tek bir pencere olarak kabul eder. Ancak genellikle içine aşağıdaki gibi ek parametreler alır:

PARTITION BY

PARTITION BY ifadesi, sonuç kümesini mantıksal bölümlere ayırır. Her bölüm, pencere fonksiyonunun bağımsız olarak üzerinde çalışacağı bir mini veri kümesi gibidir. Örneğin, çalışanları departmanlarına göre bölümlere ayırırsanız, bir departmanın ortalama maaşı sadece o departman içindeki çalışanlar üzerinden hesaplanır.


SELECT
    CalisanAdi,
    Departman,
    Maas,
    AVG(Maas) OVER (PARTITION BY Departman) AS DepartmanOrtalamaMaas
FROM
    Calisanlar;

Bu örnekte, her çalışanın kendi maaşı ve ait olduğu departmanın ortalama maaşı aynı sorgu içinde listelenir.

ORDER BY

ORDER BY, PARTITION BY ile oluşturulan her bir bölüm içindeki satırların sıralamasını belirler. Bu sıralama, özellikle sıralama (ranking) fonksiyonları (ROW_NUMBER(), RANK()) ve kümülatif toplamlar veya hareketli ortalamalar gibi sıralamaya bağımlı hesaplamalar için kritiktir.


SELECT
    SatisID,
    SatisTarihi,
    SatisTutari,
    SUM(SatisTutari) OVER (ORDER BY SatisTarihi) AS KümülatifToplamSatis
FROM
    Satislar;

Bu sorgu, satış tarihine göre sıralanmış kümülatif toplam satışı gösterir.

ROWS / RANGE BETWEEN (Pencere Çerçevesi)

ROWS BETWEEN veya RANGE BETWEEN, PARTITION BY ve ORDER BY ile belirlenen pencere içinde, fonksiyonun tam olarak hangi alt satır kümesi üzerinde işlem yapacağını tanımlar. Bu, “pencere çerçevesi” olarak adlandırılır. Örneğin, “geçerli satır ve önceki 2 satır” gibi özel bir aralık belirtebilirsiniz. En yaygın kullanımlarından bazıları şunlardır:

  • UNBOUNDED PRECEDING AND CURRENT ROW: Bölümün başından geçerli satıra kadar. (Kümülatif toplamlar için idealdir.)
  • N PRECEDING AND M FOLLOWING: Geçerli satırdan N satır öncesi ile M satır sonrası arası.
  • UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: Tüm bölümü kapsar. (PARTITION BY ile kullanılırsa tüm bölüm için tek bir değer döndürür.)

SELECT
    SatisTarihi,
    SatisTutari,
    AVG(SatisTutari) OVER (ORDER BY SatisTarihi ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS Son3GunlukOrtalama
FROM
    GunlukSatislar;

Bu örnek, satış tarihine göre sıralanmış son 3 günlük (mevcut gün dahil) satışların hareketli ortalamasını hesaplar.

Pencere Fonksiyonlarının Türleri

1. Aggregate Pencere Fonksiyonları

Geleneksel aggregate fonksiyonları (SUM(), AVG(), COUNT(), MIN(), MAX()) OVER() yan tümcesi ile kullanıldığında pencere fonksiyonu haline gelir. Bu sayede, aggregate değerini her bir detay satırı için hesaplayabiliriz.


SELECT
    UrunAdi,
    Kategori,
    SatisMiktari,
    SUM(SatisMiktari) OVER (PARTITION BY Kategori) AS KategoriToplamSatis,
    AVG(SatisMiktari) OVER (PARTITION BY Kategori) AS KategoriOrtalamaSatis
FROM
    UrunSatislari;

Her ürün satırında, ürünün kendi satış miktarının yanı sıra ait olduğu kategorinin toplam ve ortalama satış miktarları da gösterilir.

2. Sıralama (Ranking) Pencere Fonksiyonları

Bu fonksiyonlar, belirli bir pencere içindeki satırlara sıralama veya numara atar.

  • ROW_NUMBER(): Her satıra benzersiz bir sıra numarası atar. Aynı değerlere sahip satırlar farklı sıralar alır.
  • RANK(): Aynı değerlere sahip satırlara aynı sırayı atar ve sonraki sıraları atlar (boşluk bırakır).
  • DENSE_RANK(): Aynı değerlere sahip satırlara aynı sırayı atar, ancak sonraki sıraları atlamaz (boşluk bırakmaz).
  • NTILE(n): Satırları belirtilen ‘n’ sayıda gruba böler ve her gruba bir numara atar.

SELECT
    CalisanAdi,
    Departman,
    Maas,
    ROW_NUMBER() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciMaasSirasi,
    RANK() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciRank,
    DENSE_RANK() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciDenseRank
FROM
    Calisanlar;

Bu sorgu, her departman içinde maaşa göre sıralanmış çalışanların farklı sıralama türlerini gösterir.

3. Analitik (Value) Pencere Fonksiyonları

Bu fonksiyonlar, geçerli satırdan önceki, sonraki veya penceredeki ilk/son satırın değerlerini almanızı sağlar.

  • LAG(expression, offset, default): Geçerli satırdan belirtilen ‘offset’ kadar önceki satırın değerini döndürür.
  • LEAD(expression, offset, default): Geçerli satırdan belirtilen ‘offset’ kadar sonraki satırın değerini döndürür.
  • FIRST_VALUE(expression): Pencere içindeki ilk satırın değerini döndürür.
  • LAST_VALUE(expression): Pencere içindeki son satırın değerini döndürür.

SELECT
    SatisTarihi,
    SatisTutari,
    LAG(SatisTutari, 1, 0) OVER (ORDER BY SatisTarihi) AS OncekiGunSatisTutari,
    LEAD(SatisTutari, 1, 0) OVER (ORDER BY SatisTarihi) AS SonrakiGunSatisTutari
FROM
    GunlukSatislar;

Bu örnek, her günün satış tutarını, bir önceki günkü ve bir sonraki günkü satış tutarlarıyla karşılaştırmak için LAG ve LEAD fonksiyonlarını kullanır.

Pencere Fonksiyonlarının Pratik Kullanım Alanları ve Faydaları

  • Kümülatif Toplamlar ve Hareketli Ortalamalar: Zaman serisi verilerinde belirli bir ana kadar olan toplamları veya belirli bir dönemdeki ortalamaları kolayca hesaplama.
  • Gruplar İçinde En İyi/En Kötü N Değerleri Bulma: Her departmanda en yüksek maaş alan ilk 3 çalışanı bulmak gibi.
  • Satır Karşılaştırmaları: Bir önceki veya sonraki dönemdeki değerlerle karşılaştırmalar yaparak trend analizi yapma (örneğin aylık satış büyümesi).
  • Yüzdelik Dilimler ve Percentil Hesaplamaları: NTILE() ile veriyi eşit gruplara bölme veya PERCENT_RANK() gibi fonksiyonlarla değerlerin konumunu belirleme.
  • Karmaşık İş Mantıklarını Basitleştirme: Birden fazla alt sorgu veya kendi kendine join (self-join) gerektiren senaryoları tek bir pencere fonksiyonuyla daha okunabilir ve performanslı hale getirme.

SQL Pencere Fonksiyonları, veri analistleri ve geliştiriciler için güçlü ve esnek bir araç setidir. Geleneksel aggregate fonksiyonlarının veya GROUP BY ifadesinin yetersiz kaldığı durumlarda, veriye derinlemesine inerek karmaşık iş mantıklarını çözümlemede önemli bir rol oynarlar. Bu fonksiyonları etkin bir şekilde kullanmak, daha anlamlı raporlar oluşturmanızı, performansı artırmanızı ve SQL sorgularınızı daha okunabilir hale getirmenizi sağlayacaktır.


SQL’de CTE (Common Table Expressions)

SQL geliştiricilerinin karmaşık sorguları daha anlaşılır, yönetilebilir ve modüler hale getirmek için kullandığı güçlü bir özelliktir CTE (Common Table Expressions). Geçici, adlandırılmış bir sonuç kümesi tanımlayarak, aynı sorgu içinde birden fazla kez başvurulabilen mantıksal birimler oluşturulmasına olanak tanır. Bu sayede, iç içe geçmiş alt sorguların yarattığı karmaşa azalırken, kodun genel kalitesi ve performansı önemli ölçüde artırılır. CTE’ler özellikle hiyerarşik verilerin işlenmesinde vazgeçilmez bir rol oynar.

CTE Nedir?

Common Table Expression (CTE), SQL Server, PostgreSQL, MySQL (8.0 ve üzeri), Oracle gibi modern ilişkisel veritabanı yönetim sistemlerinde desteklenen, geçici ve adlandırılmış bir sonuç kümesidir. Bu sonuç kümesi, bir SELECT, INSERT, UPDATE veya DELETE deyiminin yürütme kapsamı içinde tanımlanır ve yalnızca o deyimin süresince varlığını sürdürür. CTE’ler, sorguyu daha küçük, daha okunabilir mantıksal parçalara ayırarak karmaşıklığı azaltmayı amaçlar. İç içe geçmiş alt sorguların yerine daha düz bir yapı sunarak sorgu akışını takip etmeyi kolaylaştırır.

CTE’leri Neden Kullanmalıyız?

CTE’ler, sadece karmaşık sorguları basitleştirmekle kalmaz, aynı zamanda SQL kodunuzun genel kalitesini artıran birçok avantaj sunar:

  • Okunabilirlik ve Anlaşılabilirlik: Büyük ve karmaşık sorguları daha küçük, adlandırılmış mantıksal birimlere böler. Her bir CTE, belirli bir alt görevi yerine getirdiği için sorgunun genel akışı ve amacı daha kolay anlaşılır hale gelir. Bu, kod incelemelerini ve ekip içi işbirliğini kolaylaştırır.
  • Sürdürülebilirlik ve Hata Ayıklama: Modüler yapı sayesinde, bir hata durumunda veya mantık değişikliğinde ilgili CTE’yi bulmak ve üzerinde değişiklik yapmak çok daha hızlı ve güvenlidir. Tek bir büyük sorguda hata ayıklamak yerine, her bir CTE’yi bağımsız olarak test edebilirsiniz.
  • Modülerlik ve Tekrar Kullanım: Bir CTE, tanımlandığı sorgu içinde birden fazla kez kullanılabilir. Bu, aynı alt sorgunun birden fazla yerde tekrarlanmasını önler, böylece kod tekrarını azaltır ve tutarlılığı artırır.
  • Özyinelemeli Sorgular: CTE’lerin en güçlü yönlerinden biri, özyinelemeli (recursive) sorgular yazma yeteneğidir. Bu özellik, hiyerarşik verileri (organizasyon şemaları, ağaç yapıları, malzeme listeleri gibi) işlemek için vazgeçilmezdir. Bir CTE kendi kendisine başvurarak bir hiyerarşiyi adım adım gezebilir.
  • Performans İyileştirmeleri (Dolaylı Olarak): Doğrudan performans artışı garanti etmese de, daha okunabilir ve optimize edilebilir sorgular yazmaya teşvik eder. Veritabanı optimizasyoncusu, iyi yapılandırılmış CTE’leri genellikle verimli bir şekilde işleyebilir.

CTE Tanımlama Yöntemi ve Yapısı

Bir CTE, WITH anahtar kelimesi kullanılarak tanımlanır. Temel sözdizimi aşağıdaki gibidir:

WITH CTE_Adi (Kolon1, Kolon2, ...) AS (
    SELECT Kolon1, Kolon2, ...
    FROM TabloAdi
    WHERE Sart
)
SELECT *
FROM CTE_Adi
WHERE BaskaSart;
  • WITH: CTE tanımlamaya başladığımızı gösterir.
  • CTE_Adi: Oluşturduğunuz geçici sonuç kümesine verdiğiniz isimdir. Bu isim, aynı ana sorgu içinde başka yerlerde kullanılabilir.
  • (Kolon1, Kolon2, ...) (İsteğe bağlı): CTE’nin döndüreceği sütunlar için takma adlar (alias) tanımlanabilir. Bu tanımlanmazsa, içindeki SELECT deyiminin sütun adları kullanılır.
  • AS: CTE’nin tanımının başladığını belirtir.
  • (SELECT ...): Bu parantez içindeki SELECT deyimi, CTE’nin temelini oluşturan ve döndürülecek geçici sonuç kümesini oluşturan sorgudur.

Bir CTE tanımlandıktan sonra, ana SELECT, INSERT, UPDATE veya DELETE deyiminde bir tablo gibi kullanılabilir.

Basit Bir CTE Örneği

Aşağıdaki örnekte, ortalama maaşın üzerinde kazanan çalışanları bulan basit bir CTE kullanımını inceleyelim. Önce CTE kullanmadan, sonra CTE ile karşılaştıralım.

Örnek Veri Seti:

CREATE TABLE Calisanlar (
    CalisanID INT PRIMARY KEY,
    Ad NVARCHAR(50),
    Soyad NVARCHAR(50),
    Departman NVARCHAR(50),
    Maas DECIMAL(10, 2)
);

INSERT INTO Calisanlar (CalisanID, Ad, Soyad, Departman, Maas) VALUES
(1, 'Ali', 'Can', 'IT', 60000),
(2, 'Ayşe', 'Demir', 'İK', 55000),
(3, 'Mehmet', 'Yılmaz', 'IT', 75000),
(4, 'Elif', 'Kaya', 'Muhasebe', 50000),
(5, 'Deniz', 'Tunç', 'IT', 62000),
(6, 'Kemal', 'Uslu', 'Muhasebe', 58000),
(7, 'Zeynep', 'Ateş', 'İK', 70000);

CTE Kullanmadan Sorgu:

SELECT Ad, Soyad, Maas
FROM Calisanlar
WHERE Maas > (SELECT AVG(Maas) FROM Calisanlar);

CTE Kullanarak Sorgu:

WITH OrtalamaMaas AS (
    SELECT AVG(Maas) AS GenelOrtalamaMaas
    FROM Calisanlar
)
SELECT c.Ad, c.Soyad, c.Maas
FROM Calisanlar c, OrtalamaMaas om
WHERE c.Maas > om.GenelOrtalamaMaas;

Yukarıdaki CTE örneğinde, OrtalamaMaas adında geçici bir sonuç kümesi oluşturduk. Bu CTE, tüm çalışanların ortalama maaşını hesaplar. Daha sonra ana sorgu, bu OrtalamaMaas CTE’sini kullanarak, maaşı genel ortalamanın üzerinde olan çalışanları listeler. Bu yaklaşım, sorgunun ne yaptığını daha açık bir şekilde ifade eder ve iç içe geçmiş alt sorguların karmaşıklığını ortadan kaldırır.

Birden Fazla CTE Kullanımı

Bir WITH deyimi içinde birden fazla CTE tanımlayabilir ve bunları virgülle ayırabilirsiniz. Ayrıca, sonraki bir CTE, kendisinden önce tanımlanmış bir CTE’ye başvurabilir.

WITH DepartmanOrtalamaMaaslari AS (
    SELECT Departman, AVG(Maas) AS OrtalamaDepartmanMaasi
    FROM Calisanlar
    GROUP BY Departman
),
YuksekMaasliCalisanlar AS (
    SELECT c.Ad, c.Soyad, c.Departman, c.Maas, dom.OrtalamaDepartmanMaasi
    FROM Calisanlar c
    INNER JOIN DepartmanOrtalamaMaaslari dom ON c.Departman = dom.Departman
    WHERE c.Maas > dom.OrtalamaDepartmanMaasi
)
SELECT Ad, Soyad, Departman, Maas, OrtalamaDepartmanMaasi
FROM YuksekMaasliCalisanlar
ORDER BY Departman, Maas DESC;

Bu örnekte, önce her departmanın ortalama maaşını hesaplayan DepartmanOrtalamaMaaslari CTE’si oluşturulur. Ardından, bu CTE’ye başvurarak, kendi departman ortalamasının üzerinde maaş alan çalışanları bulan YuksekMaasliCalisanlar CTE’si tanımlanır. Son olarak, ana sorgu bu ikinci CTE’den veri çeker. Bu zincirleme yapı, daha karmaşık iş mantıklarını adım adım uygulamak için idealdir.

Özyinelemeli (Recursive) CTE’ler

Özyinelemeli CTE’ler, hiyerarşik veya ağaç yapısındaki verileri (örneğin, bir organizasyon şeması, ürün bileşenleri listesi veya sosyal ağ bağlantıları) sorgulamak için kullanılır. Bir özyinelemeli CTE, kendi kendisine başvurarak belirli bir koşul karşılanana kadar tekrar tekrar çalışır.

Özyinelemeli bir CTE iki ana bölümden oluşur:

  1. Anchor Member (Çapa Üyesi): Özyinelemenin başlangıç noktasını belirler. Genellikle bir SELECT deyimidir ve ilk veri kümesini döndürür. Bu kısım tek başına çalışabilir.
  2. Recursive Member (Özyinelemeli Üye): Çapa üyesi tarafından döndürülen ve daha önce özyinelemeli üye tarafından döndürülmüş sonuçlara atıfta bulunur. Bu kısım, bir UNION ALL veya UNION operatörü ile çapa üyesiyle birleştirilir.

Bir özyinelemeli CTE’nin sonlanmasını sağlamak için bir bitiş koşulu (genellikle bir WHERE maddesi içinde) tanımlamak çok önemlidir, aksi takdirde sonsuz döngüye girilebilir.

Özyinelemeli CTE Örneği: Çalışan Hiyerarşisi

Bir organizasyon şemasındaki çalışanların yöneticilerini takip ederek tam hiyerarşiyi bulalım.

CREATE TABLE Personel (
    PersonelID INT PRIMARY KEY,
    Ad NVARCHAR(50),
    YoneticisiID INT NULL REFERENCES Personel(PersonelID)
);

INSERT INTO Personel (PersonelID, Ad, YoneticisiID) VALUES
(1, 'CEO Ayşe', NULL),
(2, 'Genel Md. Ali', 1),
(3, 'Departman Md. Can', 2),
(4, 'Takım Lideri Elif', 3),
(5, 'Çalışan Mehmet', 4),
(6, 'Çalışan Zeynep', 4),
(7, 'Departman Md. Deniz', 2),
(8, 'Takım Lideri Murat', 7),
(9, 'Çalışan Pınar', 8);
WITH Hiyerarsi AS (
    -- Çapa Üyesi: En üst yöneticiyi bul
    SELECT
        PersonelID,
        Ad,
        YoneticisiID,
        1 AS Seviye,
        CAST(Ad AS NVARCHAR(MAX)) AS Yol
    FROM Personel
    WHERE YoneticisiID IS NULL

    UNION ALL

    -- Özyinelemeli Üye: Altındaki çalışanları bul
    SELECT
        p.PersonelID,
        p.Ad,
        p.YoneticisiID,
        h.Seviye + 1 AS Seviye,
        CAST(h.Yol + ' -> ' + p.Ad AS NVARCHAR(MAX)) AS Yol
    FROM Personel p
    INNER JOIN Hiyerarsi h ON p.YoneticisiID = h.PersonelID
)
SELECT PersonelID, Ad, YoneticisiID, Seviye, Yol
FROM Hiyerarsi
ORDER BY Seviye, PersonelID;

Bu özyinelemeli CTE’de, Hiyerarsi CTE’si iki bölümden oluşur: Çapa üyesi, yöneticisi olmayan (NULL) personeli (CEO) bulur. Özyinelemeli üye ise, önceki seviyedeki yöneticilere bağlı olan personeli bulur ve her yinelemede Seviye‘yi artırır ve Yol‘u günceller. Bu süreç, hiyerarşide daha fazla alt seviye kalmayana kadar devam eder ve tam organizasyon şemasını ortaya çıkarır.

CTE’leri Ne Zaman Kullanmamalıyız?

CTE’ler güçlü bir araç olsa da, her durumda en iyi çözüm olmayabilir:

  • Basit Sorgular: Çok basit, tek satırlık bir alt sorgunun yeterli olduğu durumlarda CTE kullanmak gereksiz karmaşıklık yaratabilir. Bir CTE’nin getirdiği fayda, sorgunun karmaşıklığıyla doğru orantılıdır.
  • Performans Odaklı Büyük Geçici Veri Kümeleri: Eğer ara sonuç kümeniz çok büyükse ve bu sonuç kümesi üzerinde indeksleme veya istatistik toplama gibi işlemler gerekiyorsa, geçici tablolar (#temp_table) veya tablo değişkenleri (@table_variable) daha iyi performans gösterebilir. CTE’ler genellikle optimize edici tarafından türetilmiş tablolar (derived tables) gibi işlenir ve kendi başlarına indeksleme veya istatistik tutmazlar. Ancak modern veritabanı motorları, CTE’leri akıllıca optimize edebilir.
  • Birden Fazla Sorguda Kullanım: Eğer aynı geçici sonuç kümesini farklı, birbirinden bağımsız sorgularda kullanmanız gerekiyorsa, CTE yerine bir View (Görünüm) veya kalıcı bir tablo oluşturmak daha uygun olabilir. CTE’ler yalnızca tanımlandıkları ana sorgunun yaşam döngüsü boyunca var olurlar.

Sonuç

SQL’de CTE’ler (Common Table Expressions), karmaşık sorgu yazımını basitleştiren ve kod kalitesini artıran vazgeçilmez bir özelliktir. Okunabilirliği, sürdürülebilirliği ve modülerliği sayesinde geliştiricilere büyük kolaylıklar sunar. Özellikle özyinelemeli CTE’ler, hiyerarşik verilerin etkili bir şekilde işlenmesi için kritik bir araçtır. Doğru kullanıldığında, SQL sorgularınızın daha şeffaf, yönetilebilir ve güçlü olmasını sağlar, böylece genel veritabanı etkileşiminizi geliştirir. CTE’leri benimsemek, daha iyi SQL kodu yazmanın önemli bir adımıdır.


SQL’de Performans Ölçümü

Veritabanı sistemleri, modern uygulamaların bel kemiğini oluşturur ve bunların performansı, genel sistem hızını ve kullanıcı deneyimini doğrudan etkiler. SQL’de performans ölçümü, veritabanının sağlığını ve etkinliğini anlamak, olası darboğazları belirlemek ve sistemin yanıt sürelerini optimize etmek için kritik bir süreçtir. Bu, uygulamaların sorunsuz çalışmasını ve ölçeklenebilirliğini sağlamanın temelini oluşturur. Etkili performans ölçümü, sistem kaynaklarının daha verimli kullanılmasını sağlar.

SQL Performans Ölçümü Neden Önemlidir?

Veritabanı performansı, bir uygulamanın genel performansı için belirleyici bir faktördür. Yavaş çalışan SQL sorguları, kullanıcıların beklemesine, uygulamanın donmasına ve hatta sistemin tamamen çökmesine neden olabilir. Performans ölçümü, bu tür sorunları ortaya çıkmadan önce veya hızlıca tespit edip çözmek için proaktif bir yaklaşım sunar. İşte temel nedenler:

  • Kullanıcı Deneyimi: Hızlı yanıt veren uygulamalar, kullanıcı memnuniyetini artırır.
  • Sistem Kararlılığı: Yüksek performanslı bir veritabanı, yoğun yük altında bile kararlı çalışır.
  • Kaynak Verimliliği: Performans analizi, CPU, bellek ve disk gibi değerli sunucu kaynaklarının gereksiz yere tüketilmesini engeller.
  • Ölçeklenebilirlik: Gelecekteki büyüme ve artan veri hacimleri için veritabanının nasıl optimize edileceğini gösterir.
  • Maliyet Tasarrufu: Verimli çalışan bir veritabanı, daha az donanım kaynağı gerektirebilir veya mevcut kaynaklardan daha iyi faydalanabilir.

Performans Ölçümünde Temel Metrikler

SQL performansını değerlendirirken çeşitli metrikleri izlemek hayati önem taşır. Bu metrikler, sorunun kaynağını anlamamıza ve doğru optimizasyon stratejilerini belirlememize yardımcı olur.

Sorgu Yürütme Süresi

Belirli bir SQL sorgusunun ne kadar sürede tamamlandığı, en temel performans göstergelerinden biridir. Yavaş sorgular genellikle darboğazın ana kaynağıdır. Bu süreye, veritabanının sorguyu işlemesi, veriyi getirmesi ve sonucu geri döndürmesi dahildir. Ortalama ve maksimum yürütme süreleri, trendleri anlamak için önemlidir.

CPU ve Bellek Kullanımı

Veritabanı sunucusunun CPU ve bellek kullanımı, genel sistem yükünü gösterir. Yüksek CPU kullanımı, karmaşık veya verimsiz sorguların çalıştırıldığını, yetersiz indekslemeyi veya yetersiz donanımı işaret edebilir. Bellek (RAM) kullanımı ise, sorguların önbellekleme ve veri işlemesi için ne kadar kaynak tükettiğini gösterir. Yetersiz bellek, disk I/O’sunu artırarak performansı düşürebilir.

I/O İşlemleri ve Disk Performansı

Veritabanları, verileri diskten okuma (read) ve diske yazma (write) işlemleriyle yoğun bir şekilde çalışır. Yüksek I/O gecikmesi veya yüksek I/O bekleme süreleri, disk alt sisteminin bir darboğaz olduğunu gösterir. Bu durum, yavaş diskler, yetersiz disk yapılandırması veya disk tabanlı işlemlerin yoğunluğundan kaynaklanabilir. Örneğin, sık sık tam tablo taramaları yapmak, I/O yükünü önemli ölçüde artırır.

Kilitlemeler ve Kilitlenmeler (Deadlock)

Çok kullanıcılı ortamlarda, aynı verilere erişen birden fazla işlem, veri tutarlılığını sağlamak için birbirlerini kilitleyebilir. Kilitlemeler (locks), bir işlemin başka bir işlemi belirli bir kaynağa erişmek için bekletmesi durumudur. Aşırı kilitleme, performansı düşürür. Kilitlenmeler (deadlocks) ise, iki veya daha fazla işlemin sonsuz döngüye girerek birbirlerini beklemesi ve hiçbirinin ilerleyememesi durumudur. Bu durumlar ciddi performans sorunlarına yol açar ve veritabanı tarafından otomatik olarak çözülmeye çalışılsa da, uygulamanın genel sağlığını olumsuz etkiler.

Ağ Gecikmesi

Veritabanı sunucusu ile uygulama sunucusu arasındaki ağ gecikmesi, sorgu yanıt sürelerini etkileyebilir. Özellikle bulut ortamlarında veya dağıtılmış sistemlerde, yüksek ağ gecikmesi veya bant genişliği kısıtlamaları performansı düşürebilir. Uygulama ile veritabanı arasındaki bağlantı kalitesi ve konfigürasyonu bu noktada kritik rol oynar.

Performans Ölçüm Araçları ve Teknikleri

Performansı ölçmek için çeşitli araçlar ve teknikler mevcuttur. Bunlar, veritabanı sistemine ve işletim sistemine göre farklılık gösterebilir.

Veritabanına Özgü Araçlar

  • SQL Server:
    • SQL Server Management Studio (SSMS) Aktivite Monitörü: Anlık CPU, bellek, I/O ve kilitleme bilgilerini sağlar.
    • Query Store (Sorgu Mağazası): SQL Server 2016 ve sonrası sürümlerde, veritabanının sorgu işleme geçmişini, planlarını ve çalışma zamanı istatistiklerini izlemek için kullanılır. Yavaşlayan sorguları tespit etmek için çok güçlü bir araçtır.
    • SQL Server Profiler/Extended Events: Gerçek zamanlı olarak veritabanında gerçekleşen olayları (sorgu başlatma/bitirme, hata, kilitleme vb.) izlemek ve kaydetmek için kullanılır. Extended Events, daha az overhead ile daha modern bir alternatiftir.
  • MySQL:
    • Performance Schema: MySQL 5.5 ve sonrası sürümlerde, sunucunun içsel çalışması hakkında detaylı bilgiler (sorgu yürütme, I/O, kilitleme vb.) sağlar.
    • Slow Query Log: Belirli bir süreyi aşan sorguları kaydeder. Yavaş sorguları belirlemek için temel bir araçtır.
    • MySQL Workbench Performance Reports: Performans şeması verilerini görselleştiren ve analiz eden bir arayüze sahiptir.
  • PostgreSQL:
    • pg_stat_activity: Mevcut bağlantılar, çalışan sorgular ve durumları hakkında bilgi sağlar.
    • pg_stat_statements: Çalıştırılan tüm sorguların istatistiklerini (yürütme süresi, çağrı sayısı vb.) toplar.
    • EXPLAIN / EXPLAIN ANALYZE: Sorgu planlarını detaylı olarak inceler.

Sorgu Planı Analizi (EXPLAIN / EXPLAIN ANALYZE)

EXPLAIN (veya PostgreSQL’de EXPLAIN ANALYZE) komutu, bir SQL sorgusunun veritabanı tarafından nasıl yürütüleceğini gösteren bir “yürütme planı” sağlar. Bu plan, sorgunun veriye nasıl eriştiğini (tam tablo taraması, indeks taraması), tabloları nasıl birleştirdiğini (join tipleri) ve hangi maliyetle çalıştığını ortaya koyar. Planı okuyarak, indeks eksikliklerini, verimsiz birleşimleri veya gereksiz taramaları tespit edebiliriz.

EXPLAIN SELECT * FROM Customers WHERE City = 'Ankara';

Bu komutun çıktısında, veritabanının Customers tablosunu nasıl taradığını, bir indeks kullanıp kullanmadığını ve tahmin edilen maliyetini görebilirsiniz. Eğer tam tablo taraması (Full Table Scan) yapılıyorsa ve ilgili sütunda indeks yoksa, bu bir optimizasyon fırsatı olabilir.

İşletim Sistemi Araçları

Veritabanı sunucusunun çalıştığı işletim sistemindeki araçlar da genel sistem performansını izlemek için önemlidir:

  • Linux:
    • top, htop: CPU, bellek, süreçler ve I/O hakkında genel bir görünüm sağlar.
    • iostat: Disk I/O istatistiklerini gösterir.
    • vmstat: Sanal bellek, disk, CPU ve süreç aktivitesini izler.
  • Windows:
    • Görev Yöneticisi: CPU, bellek, disk ve ağ kullanımı hakkında temel bilgiler sunar.
    • Performans İzleyicisi (Performance Monitor): Çok daha detaylı sistem metriklerini (disk kuyruk uzunluğu, sayfa hataları vb.) izlemek için kullanılır.

Üçüncü Parti İzleme Çözümleri

Datadog, New Relic, SolarWinds Database Performance Analyzer (DPA) gibi üçüncü parti araçlar, birden fazla veritabanı ve sunucu türünü tek bir arayüzden izleme, gelişmiş uyarılar ve otomatik performans analizi yetenekleri sunar. Bu araçlar genellikle daha kapsamlı bir izleme ve raporlama yeteneği sağlar.

Bottleneck Tespiti ve Analizi

Performans ölçümü, darboğazları (bottlenecks) tespit etmenin ilk adımıdır. Bir darboğaz, sistemin genel performansını sınırlayan tek bir bileşendir. Tespit edildikten sonra analiz edilmeli ve çözülmelidir.

Yavaş Sorguların Belirlenmesi

En sık veya en uzun süre çalışan sorguları belirlemek, optimizasyon çalışmalarının en önemli başlangıç noktasıdır. Query Store, Slow Query Log veya pg_stat_statements gibi araçlar bu sorguları kolayca tespit etmenizi sağlar. Yavaş sorgular genellikle karmaşık JOIN’ler, alt sorgular, uygunsuz fonksiyon kullanımları veya büyük veri setleri üzerinde yapılan işlemlerden kaynaklanır.

Eksik veya Yanlış İndeksler

İndeksler, veritabanı sorgularının hızını önemli ölçüde artırabilir. Eksik veya yanlış tanımlanmış indeksler, veritabanının büyük veri tablolarını baştan sona taramasına neden olarak I/O yükünü artırır ve sorgu sürelerini uzatır. Performans araçları, hangi indekslerin kullanıldığını veya hangi indekslerin eksik olduğunu gösterebilir. Örneğin, WHERE, JOIN ve ORDER BY koşullarında kullanılan sütunlara indeks eklemek genellikle performansı artırır.

Veritabanı Yapılandırma Hataları

Veritabanı sunucusunun veya veritabanının kendisinin yanlış yapılandırılması da performans sorunlarına yol açabilir. Örneğin, yetersiz bellek ayarları, yanlış önbellekleme boyutları, Transaction Log ayarları veya paralellik ayarları performansı olumsuz etkileyebilir. Veritabanı yönetim sistemlerinin (DBMS) birçok yapılandırma parametresi vardır ve bunların doğru ayarlanması kritik öneme sahiptir.

Donanım Sınırlamaları

Bazen sorun yazılımsal olmaktan ziyade donanımsaldır. Yetersiz CPU gücü, az RAM veya yavaş diskler, veritabanı performansını doğrudan sınırlar. Özellikle yüksek trafikli sistemlerde, sunucunun donanım kapasitesi, veritabanının kaldırabileceği yükü belirler. İşletim sistemi araçları, donanım kaynaklarının ne kadarının kullanıldığını göstererek bu tür sınırlamaların tespit edilmesine yardımcı olur.

SQL performans ölçümü ve takibi, bir veritabanının sağlıklı ve verimli bir şekilde çalışması için sürekli dikkat gerektiren bir disiplindir. Metriklerin düzenli olarak izlenmesi, doğru araçların kullanılması ve elde edilen verilerin etkin bir şekilde analizi, olası sorunları proaktif olarak tespit etmeye olanak tanır. Performans darboğazlarının belirlenmesi ve ortadan kaldırılması, daha hızlı uygulamalar ve daha iyi kullanıcı deneyimi anlamına gelir. Sürekli optimizasyon, veritabanı sistemlerinin uzun vadeli başarısının anahtarıdır.


SQL’de Backup, Restore ve Güvenlik

Veritabanları, modern işletmelerin can damarıdır ve kritik verileri barındırır. Bu nedenle, SQL veritabanlarında veri kaybını önlemek, iş sürekliliğini sağlamak ve güvenlik açıklarını kapatmak hayati önem taşır. Yedekleme, geri yükleme ve güvenlik stratejileri, olası felaket senaryolarına karşı bir kalkan görevi görür. Bu üç unsur, verilerinizin bütünlüğünü, erişilebilirliğini ve gizliliğini korumak için ayrılmaz bir bütündür. Bu makale, SQL veritabanı yönetiminin bu temel taşlarını derinlemesine inceleyecektir.

Veritabanı Yedekleme (Database Backup)

Veritabanı yedeklemesi, bir veritabanının belirli bir zamandaki kopyasını oluşturma işlemidir. Bu işlem, donanım arızaları, yazılım hataları, insan kaynaklı hatalar, siber saldırılar veya doğal afetler gibi öngörülemeyen durumlar karşısında veri kaybını önlemek için kritik öneme sahiptir. Etkili bir yedekleme stratejisi, iş sürekliliğini garantileyen temel unsurdur.

Yedekleme Türleri

  • Tam Yedekleme (Full Backup): Tüm veritabanının, işlem günlükleri dahil olmak üzere eksiksiz bir kopyasını oluşturur. Geri yükleme için tek başına yeterlidir ancak boyutu büyük olabilir ve tamamlanması uzun sürebilir.
  • Diferansiyel Yedekleme (Differential Backup): Son tam yedeklemeden bu yana değişen tüm verileri içerir. Tam yedeklemeye göre daha küçüktür ve daha hızlı tamamlanır. Geri yükleme için son tam yedekleme ve son diferansiyel yedekleme gerekir.
  • İşlem Günlüğü Yedeklemesi (Transaction Log Backup): Sadece işlem günlüğünü yedekler, bu da veritabanının bir felaket anında belirli bir zamana (point-in-time) geri yüklenmesine olanak tanır. Genellikle tam veya diferansiyel yedeklemeler arasında sık aralıklarla alınır.
  • Dosya/Dosya Grubu Yedeklemesi (File/Filegroup Backup): Büyük veritabanlarında, veritabanının tamamı yerine belirli dosya veya dosya gruplarını yedeklemek için kullanılır.

Yedekleme Stratejileri ve En İyi Uygulamalar

  • Sıklık ve Saklama Politikası: Yedeklemelerin ne sıklıkla alınacağı ve ne kadar süreyle saklanacağı, verinin kritikliği ve kurtarma noktası hedefi (RPO – Recovery Point Objective) ile belirlenmelidir. Örneğin, kritik üretim veritabanları için her 15 dakikada bir işlem günlüğü yedeklemesi alınabilir.
  • Yedekleme Ortamları: Yedeklemeler, ana veritabanından farklı fiziksel konumlarda (ağ depolama, bulut depolama, manyetik bant vb.) saklanmalıdır. 3-2-1 kuralı (3 kopya, 2 farklı ortam, 1 offsite) iyi bir yaklaşımdır.
  • Yedeklemelerin Doğrulanması: Alınan yedeklemelerin gerçekten geçerli ve geri yüklenebilir olduğundan emin olmak için düzenli olarak test edilmesi zorunludur. Bozuk bir yedekleme, yedekleme olmamasından daha kötü olabilir.
  • Şifreleme ve Sıkıştırma: Yedekleme dosyalarını şifrelemek, hassas verilerin yetkisiz erişime karşı korunmasına yardımcı olur. Sıkıştırma ise depolama alanı gereksinimlerini azaltır.

SQL Server T-SQL Örneği

BACKUP DATABASE YourDatabaseName
TO DISK = 'C:\Backup\YourDatabaseName_Full.bak'
WITH FORMAT, NAME = 'YourDatabaseName Full Backup';

BACKUP LOG YourDatabaseName
TO DISK = 'C:\Backup\YourDatabaseName_Log.trn'
WITH NORECOVERY, NAME = 'YourDatabaseName Log Backup';

Veritabanı Geri Yükleme (Database Restore)

Yedeklemelerin asıl amacı, bir felaket durumunda veritabanını çalışır duruma geri getirmektir. Geri yükleme işlemi, doğru yedekleme serisini doğru sırayla uygulamayı gerektirir.

Geri Yükleme Süreci

  • Tam Geri Yükleme: Önce en son tam yedekleme geri yüklenir.
  • Diferansiyel Geri Yükleme (Opsiyonel): Tam yedeklemeden sonra alınmış en son diferansiyel yedekleme geri yüklenir.
  • İşlem Günlüğü Geri Yüklemesi (Opsiyonel): Diferansiyel yedeklemeden (veya tam yedeklemeden, diferansiyel yoksa) sonra alınmış işlem günlüğü yedeklemeleri, istenilen kurtarma noktasına kadar sırayla geri yüklenir.
  • Kurtarma Modu: Geri yükleme işlemi sırasında veritabanı “NORECOVERY” modunda bırakılarak daha fazla işlem günlüğü uygulanmasına olanak tanınır. En son işlem günlüğü uygulandıktan sonra “WITH RECOVERY” seçeneği ile veritabanı erişime açılır.

Önemli Hususlar

  • Zamana Yönelik Geri Yükleme (Point-in-Time Recovery): İşlem günlüğü yedeklemeleri sayesinde, veritabanını belirli bir saate veya dakikaya kadar geri yüklemek mümkündür. Bu, örneğin, yanlışlıkla silinen bir tablonun silinmeden önceki durumuna dönmek için çok değerlidir.
  • Test Etme: Geri yükleme işlemlerinin de tıpkı yedeklemeler gibi düzenli olarak test edilmesi gerekir. Bir felaket anında geri yükleme prosedürlerinin işe yaradığından emin olmak kritik öneme sahiptir.
  • Hedef Konum: Veritabanının farklı bir sunucuya veya farklı bir konuma geri yüklenmesi gerekebilir. Bu durumda dosya yolları ve adlandırmalar dikkatle yönetilmelidir.

SQL Server T-SQL Örneği

RESTORE DATABASE YourDatabaseName
FROM DISK = 'C:\Backup\YourDatabaseName_Full.bak'
WITH NORECOVERY; -- Tam yedeklemeyi NORECOVERY ile geri yükle

-- Eğer diferansiyel yedekleme varsa:
-- RESTORE DATABASE YourDatabaseName
-- FROM DISK = 'C:\Backup\YourDatabaseName_Diff.bak'
-- WITH NORECOVERY;

-- İşlem günlüğü yedeklemesini zamana yönelik geri yükleme örneği
RESTORE LOG YourDatabaseName
FROM DISK = 'C:\Backup\YourDatabaseName_Log.trn'
WITH RECOVERY, STOPAT = '2023-10-27 14:30:00'; -- Belirli bir zamana kadar geri yükle ve erişime aç

Veritabanı Güvenliği (Database Security)

Veritabanı güvenliği, verilerin gizliliğini (confidentiality), bütünlüğünü (integrity) ve erişilebilirliğini (availability) korumak için alınan önlemlerin bütünüdür. Bu, yetkisiz erişimi engellemeyi, veri manipülasyonunu önlemeyi ve veritabanı sistemlerinin sürekli çalışmasını sağlamayı kapsar.

Kimlik Doğrulama (Authentication)

  • Windows Kimlik Doğrulaması: Kullanıcıların Windows işletim sistemi kimlik bilgilerini kullanarak veritabanına erişmesine olanak tanır. Daha güvenli kabul edilir, çünkü parola politikaları merkezi olarak yönetilir.
  • SQL Server Kimlik Doğrulaması: SQL Server içinde kullanıcı adları ve parolaları tanımlanır. Parola karmaşıklığı ve sona erme politikaları uygulanmalıdır.
  • Çok Faktörlü Kimlik Doğrulama (MFA): Ek bir güvenlik katmanı olarak MFA uygulamak, yetkisiz erişimi önemli ölçüde zorlaştırır.

Yetkilendirme (Authorization)

  • Kullanıcılar ve Roller: Her kullanıcıya tek tek yetki vermek yerine, benzer yetkilere sahip kullanıcıları rollere atamak ve yetkileri rollere vermek yönetim kolaylığı sağlar.
  • Erişim İzinleri (Permissions): Kullanıcılara veya rollere belirli veritabanı nesneleri (tablolar, görünümler, saklı yordamlar) üzerinde yalnızca ihtiyaç duydukları minimum izinler (SELECT, INSERT, UPDATE, DELETE, EXECUTE vb.) verilmelidir. Bu, “en az ayrıcalık ilkesi” (principle of least privilege) olarak bilinir.
  • GRANT ve DENY Deyimleri: SQL’de izinleri vermek için GRANT, belirli bir izni açıkça reddetmek için DENY kullanılır. REVOKE ise daha önce verilmiş veya reddedilmiş bir izni geri alır.

SQL Server T-SQL Örneği

CREATE LOGIN [MyUser] WITH PASSWORD = 'StrongPassword123!', CHECK_POLICY = ON;
CREATE USER MyUser FOR LOGIN MyUser;
CREATE ROLE DataReaders;
GRANT SELECT ON SCHEMA::dbo TO DataReaders;
ALTER ROLE DataReaders ADD MEMBER MyUser;
GRANT EXECUTE ON OBJECT::dbo.MyStoredProcedure TO MyUser;

Veri Şifreleme (Data Encryption)

  • Saydam Veri Şifrelemesi (Transparent Data Encryption – TDE): Veritabanı dosyalarını diskte (at rest) şifreler. Uygulama katmanında herhangi bir değişiklik yapmadan verilerin depolandığı yerde korunmasını sağlar.
  • Her Zaman Şifrelenmiş (Always Encrypted): Hassas verilerin veritabanına gönderilmeden önce istemci uygulamasında şifrelenmesini ve veritabanı sunucusunda şifrelenmiş olarak depolanmasını sağlar. Veritabanı yöneticileri bile şifrelenmiş verilere erişemez.
  • İletişim Şifrelemesi (Encryption in Transit): SSL/TLS gibi protokoller kullanarak istemci ile sunucu arasındaki ağ trafiğini şifrelemek, Man-in-the-Middle saldırılarını önler.

Denetim (Auditing)

  • Veritabanı denetimi, belirli veritabanı olaylarının (giriş denemeleri, veri değişiklikleri, şema değişiklikleri vb.) izlenmesi ve kaydedilmesidir. Bu kayıtlar, güvenlik ihlallerinin tespiti, sorumluluk tespiti ve uyumluluk gereksinimlerinin karşılanması için hayati öneme sahiptir.
  • SQL Server Audit gibi araçlar bu amaçla kullanılır.

Ağ Güvenliği

  • Güvenlik Duvarları (Firewalls): Veritabanı sunucusuna sadece belirli IP adreslerinden veya uygulamalardan erişime izin vermek için güvenlik duvarları yapılandırılmalıdır.
  • Ağ Segmentasyonu: Veritabanı sunucuları, diğer sunuculardan ayrı, izole edilmiş ağ segmentlerinde (DMZ gibi) tutulmalıdır.

Diğer Güvenlik Uygulamaları

  • Düzenli Güncellemeler ve Yama Yönetimi: Veritabanı yazılımını ve işletim sistemini en son güvenlik yamalarıyla güncel tutmak, bilinen güvenlik açıklarının kapatılması için zorunludur.
  • Fiziksel Güvenlik: Veritabanı sunucularının bulunduğu veri merkezlerine yetkisiz fiziksel erişimin engellenmesi.
  • Hassas Veri Maskeleme/Anonimleştirme: Geliştirme veya test ortamlarında gerçek hassas veriler yerine maskelenmiş veya anonimleştirilmiş veri kullanmak.
  • Zafiyet Tarama ve Penetrasyon Testleri: Veritabanı sistemlerindeki potansiyel güvenlik açıklarını tespit etmek için düzenli taramalar ve testler yapılmalıdır.

SQL veritabanlarında yedekleme, geri yükleme ve güvenlik, sadece teknik gereklilikler değil, aynı zamanda iş sürekliliğinin ve veri korumanın temel direkleridir. Kapsamlı bir yedekleme stratejisi, düzenli geri yükleme testleri ve katı güvenlik önlemleri, veri kaybı riskini minimize ederken, olası tehditlere karşı güçlü bir savunma hattı oluşturur. Bu unsurların entegre ve proaktif bir şekilde yönetilmesi, işletmelerin verilerine güvenle sahip çıkmasını ve olası aksaklıklardan hızla kurtulmasını sağlar. Unutmayın, veri güvenliği sürekli bir çabadır.


SQL’de Normalization (Normalizasyon)

Veritabanı yönetim sistemlerinde verilerin etkin, tutarlı ve tekrarsız bir şekilde saklanması büyük önem taşır. SQL’de normalizasyon, ilişkisel veritabanı tasarımında veri tekrarını azaltmayı, veri bütünlüğünü artırmayı ve veritabanı anormalliklerini ortadan kaldırmayı amaçlayan yapısal bir süreçtir. Bu, veritabanı performansını ve güvenilirliğini önemli ölçüde etkileyen temel bir kavramdır. Doğru uygulandığında, daha yönetilebilir ve sürdürülebilir bir veritabanı sağlar.

Normalizasyon Nedir ve Neden Önemlidir?

Normalizasyon, bir ilişkisel veritabanı şemasını belirli kurallar setine göre düzenleme işlemidir. Bu kurallar, “Normal Formlar” olarak bilinir ve veritabanındaki veri tekrarını (redundancy) en aza indirmeyi, veri bütünlüğünü (data integrity) artırmayı ve anormallikleri (anomalies) ortadan kaldırmayı hedefler. Bir veritabanının anormalliklere sahip olması; veri ekleme (insertion), güncelleme (update) veya silme (deletion) işlemlerinde tutarsızlıklara yol açabilen durumlardır.

Normalizasyonun temel faydaları şunlardır:

  • Veri Tekrarının Azaltılması: Aynı verinin birden fazla yerde saklanmasını önleyerek depolama alanından tasarruf sağlar.
  • Veri Bütünlüğünün Artırılması: Tekrarlı verinin olmaması, bir veriyi güncellerken tüm kopyalarının güncellenmesini zorunlu kılmaz ve böylece tutarsızlık riskini azaltır.
  • Anormalliklerin Ortadan Kaldırılması: Ekleme, güncelleme ve silme anormalliklerinin önüne geçerek veritabanının daha güvenilir olmasını sağlar. Örneğin, bir müşterinin adresini birden fazla tabloda tutuyorsanız ve adres değiştiğinde tüm kopyaları güncellemeyi unutursanız, veri tutarsızlığı yaşanır.
  • Daha İyi Veri Yönetimi: Daha küçük, daha odaklanmış tablolar, veri yönetimini ve bakımı kolaylaştırır.
  • Daha Esnek Veritabanı Yapısı: Yeni gereksinimlere daha kolay adapte olabilen bir yapı sunar.

Normal Formlar (Normalization Forms)

Normalizasyon süreci, bir dizi “Normal Form” aracılığıyla ilerler. Her normal form, bir önceki formun gereksinimlerini karşılar ve ek kurallar getirir. En yaygın kullanılan normal formlar 1NF, 2NF ve 3NF’dir.

Birinci Normal Form (1NF – First Normal Form)

Bir tablonun 1NF’de olabilmesi için aşağıdaki koşulları sağlaması gerekir:

  • Her sütun atomik (bölünemez) değerler içermelidir. Yani, bir hücrede birden fazla değer veya virgülle ayrılmış listeler olmamalıdır.
  • Her satır benzersiz olmalıdır (yani birincil anahtara sahip olmalıdır).
  • Tekrarlayan sütun grupları içermemelidir.

Örnek: Bir Çalışanlar tablosunda TelefonNumaraları sütununda “555-1234, 555-5678” gibi birden fazla telefon numarası varsa, bu 1NF’e aykırıdır. Düzeltme için, her telefon numarası için ayrı bir satır oluşturulabilir veya ayrı bir ÇalışanTelefonları tablosu oluşturulup, ÇalışanID ile ilişkilendirilebilir.

İkinci Normal Form (2NF – Second Normal Form)

Bir tablonun 2NF’de olabilmesi için aşağıdaki koşulları sağlaması gerekir:

  • 1NF’de olmalıdır.
  • Birincil anahtar olmayan hiçbir sütun, birleşik (composite) birincil anahtarın sadece bir kısmına bağlı olmamalıdır (kısmi bağımlılık olmamalıdır). Tüm birincil anahtara tam işlevsel bağımlılık göstermelidir.

Örnek: Bir SiparişDetayları tablosunun birincil anahtarı (SiparişID, ÜrünID) olsun. Bu tabloda ÜrünAdı sütunu varsa ve ÜrünAdı sadece ÜrünID‘ye bağlıysa (SiparişID‘ye değil), bu bir kısmi bağımlılıktır ve 2NF’e aykırıdır. Düzeltme için, ÜrünID ve ÜrünAdı‘nı içeren ayrı bir Ürünler tablosu oluşturulmalıdır.

Üçüncü Normal Form (3NF – Third Normal Form)

Bir tablonun 3NF’de olabilmesi için aşağıdaki koşulları sağlaması gerekir:

  • 2NF’de olmalıdır.
  • Birincil anahtar olmayan hiçbir sütun, birincil anahtar olmayan başka bir sütuna bağlı olmamalıdır (geçişli bağımlılık olmamalıdır).

Örnek: Bir Personel tablosunda (PersonelID, Ad, Soyad, DepartmanAdı, DepartmanKodu) sütunları olsun ve PersonelID birincil anahtar olsun. Eğer DepartmanKodu, DepartmanAdı‘na bağlıysa ve DepartmanAdı da PersonelID‘ye bağlı olmayan bir nitelikse, bu bir geçişli bağımlılıktır (PersonelID -> DepartmanAdı -> DepartmanKodu). Düzeltme için, DepartmanKodu ve DepartmanAdı‘nı içeren ayrı bir Departmanlar tablosu oluşturulmalıdır.

Boyce-Codd Normal Form (BCNF)

BCNF, 3NF’nin daha güçlü bir versiyonudur. Bir tablo BCNF’de olabilmesi için:

  • 3NF’de olmalıdır.
  • Her bir belirleyici (determinant) bir aday anahtar (candidate key) olmalıdır. (Belirleyici, başka bir özniteliği veya öznitelik grubunu belirleyen bir öznitelik veya öznitelik grubudur.)

BCNF, özellikle tabloda birden fazla, örtüşen aday anahtar olduğunda ortaya çıkan belirli anormallikleri ele alır. Çoğu durumda, 3NF pratik uygulamalar için yeterli olsa da, BCNF daha yüksek düzeyde normalizasyon sağlar.

Denormalizasyon (Denormalization)

Normalizasyonun temel amacı veri bütünlüğünü ve depolama verimliliğini sağlamak olsa da, çok yüksek derecede normalleştirilmiş veritabanları karmaşık sorgular için performansı düşürebilir. Birden fazla tablonun birleştirilmesi (JOIN) gerektiren sorgular, özellikle büyük veri setlerinde zaman alıcı olabilir.

Denormalizasyon, bilerek veri tekrarı ekleyerek veya normalleştirilmiş tabloları birleştirerek veritabanının okunabilirlik ve sorgu performansını artırmayı amaçlayan bir stratejidir. Genellikle, raporlama, veri ambarı sistemleri veya yüksek okuma trafiğine sahip uygulamalar gibi belirli performans darboğazlarını çözmek için kullanılır.

Denormalizasyonun faydaları şunlardır:

  • Sorguların daha hızlı çalışması.
  • JOIN işlemlerine olan ihtiyacın azalması, sorgu karmaşıklığının azalması.
  • Raporlama ve analiz için daha kolay veri erişimi.

Ancak denormalizasyonun dezavantajları da vardır:

  • Veri tekrarının artması.
  • Veri tutarlılığını korumak için daha karmaşık güncelleme ve ekleme işlemleri gerekliliği.
  • Depolama maliyetlerinin artması.

Denormalizasyon, yalnızca performans iyileştirmesi kritik olduğunda ve potansiyel tutarsızlık riskleri dikkatlice yönetilebildiğinde düşünülmesi gereken bir tasarım kararıdır.

Sonuç

Özetle, SQL’de normalizasyon, ilişkisel veritabanlarının sağlam, tutarlı ve verimli olmasını sağlayan kritik bir süreçtir. Veri tekrarını en aza indirerek ve anormallikleri ortadan kaldırarak veritabanı bütünlüğünü artırır, depolama alanından tasarruf sağlar ve veri yönetimini kolaylaştırır. Ancak, performans gereksinimleri doğrultusunda denormalizasyon da stratejik bir seçenek olabilir. Doğru dengeyi bulmak, veritabanı tasarımında uzun vadeli başarı için hayati öneme sahiptir. Veritabanı tasarımcıları, uygulamanın ihtiyaçlarına en uygun normalizasyon seviyesini dikkatlice belirlemelidir.


SQL’de Constraint’ler ve Veri Bütünlüğü

Veritabanı yönetim sistemlerinde verilerin doğruluğu, tutarlılığı ve güvenilirliği kritik öneme sahiptir. SQL Constraint’ler, bu veri bütünlüğünü sağlamak ve istenmeyen veya geçersiz verilerin veritabanına girişini engellemek için kullanılan güçlü araçlardır. İlişkisel veritabanlarının temel taşlarından olan bu kısıtlamalar, veritabanı şemasının tanımlı kurallara uygun kalmasını garanti ederek uzun vadede veri kalitesini maksimize eder ve uygulama hatalarını minimize eder.

Veri Bütünlüğü: İlişkisel Veritabanlarının Temeli

Veri bütünlüğü, veritabanındaki bilgilerin doğruluğunu, tutarlılığını ve güvenilirliğini ifade eder. Bu, verilerin istenmeyen değişikliklerden veya bozulmalardan korunması anlamına gelir. İlişkisel veritabanlarında genellikle dört ana veri bütünlüğü türü bulunur:

  • Varlık Bütünlüğü (Entity Integrity): Her satırın bir tablonun içinde benzersiz bir şekilde tanımlanabilmesini sağlar. Genellikle PRIMARY KEY ile garanti edilir.
  • Referans Bütünlüğü (Referential Integrity): Bir tablodaki yabancı anahtar değerlerinin, ilişkili tablodaki birincil anahtar değerleriyle eşleşmesini veya NULL olmasını sağlar. FOREIGN KEY ile garanti edilir.
  • Alan Bütünlüğü (Domain Integrity): Bir sütundaki tüm değerlerin belirli bir tür, biçim ve aralık dahilinde olmasını sağlar. NOT NULL, CHECK ve DEFAULT ile desteklenir.
  • Kullanıcı Tanımlı Bütünlük (User-Defined Integrity): İş gereksinimlerine özgü ek kuralları ifade eder ve genellikle CHECK constraint’ler veya tetikleyiciler (trigger) aracılığıyla uygulanır.

SQL Constraint’ler, bu bütünlük kurallarını doğrudan veritabanı şeması düzeyinde uygulayarak, verilerin uygulamalardan bağımsız olarak tutarlı kalmasını sağlar.

SQL Constraint’ler Nelerdir ve Nasıl Çalışır?

SQL Constraint’ler, bir veritabanı tablosundaki sütunlara veya tüm tabloya uygulanan kurallar bütünüdür. Bu kurallar, veri ekleme (INSERT), güncelleme (UPDATE) veya silme (DELETE) işlemleri sırasında devreye girer ve tanımlı koşulların ihlal edilip edilmediğini kontrol eder. Eğer bir işlem bir kısıtlamayı ihlal ederse, veritabanı bu işlemi reddeder ve bir hata mesajı döndürür. Bu mekanizma, hatalı verilerin sisteme girişini engeller ve veritabanının sağlıklı yapısını korur.

Temel SQL Constraint Türleri ve Kullanımları

1. NOT NULL Constraint

NOT NULL constraint, bir sütundaki değerlerin boş (NULL) olamayacağını garanti eder. Bu, o sütun için mutlaka bir değer girilmesi gerektiğini belirtir. Özellikle kritik bilgiler içeren sütunlar (örneğin, kullanıcı adları, sipariş numaraları) için hayati öneme sahiptir.

CREATE TABLE Musteriler (
    MusteriID INT NOT NULL,
    Ad VARCHAR(50) NOT NULL,
    Soyad VARCHAR(50),
    Email VARCHAR(100)
);

2. UNIQUE Constraint

UNIQUE constraint, bir sütundaki veya sütun kümesindeki tüm değerlerin benzersiz olmasını garanti eder. İki satırın aynı değere sahip olmasını engeller. NULL değerlere izin verir, ancak her NULL değeri benzersiz kabul eder. Birden fazla UNIQUE constraint bir tabloya uygulanabilir.

CREATE TABLE Urunler (
    UrunID INT NOT NULL UNIQUE,
    UrunKodu VARCHAR(20) UNIQUE,
    UrunAdi VARCHAR(100) NOT NULL
);

3. PRIMARY KEY Constraint

PRIMARY KEY (Birincil Anahtar) constraint, bir tablodaki her satırı benzersiz bir şekilde tanımlayan bir sütun veya sütun kümesidir. PRIMARY KEY, hem NOT NULL hem de UNIQUE özelliklerini içerir. Bir tabloda yalnızca bir PRIMARY KEY olabilir ve bu, varlık bütünlüğünü sağlar.

CREATE TABLE Calisanlar (
    CalisanID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50),
    TCKimlikNo VARCHAR(11) UNIQUE -- T.C. Kimlik No da benzersiz olmalı, ama birincil anahtar değil
);

Birden fazla sütunlu bir birincil anahtar için (composite primary key):

CREATE TABLE SiparisDetay (
    SiparisID INT,
    UrunID INT,
    Miktar INT,
    PRIMARY KEY (SiparisID, UrunID)
);

4. FOREIGN KEY Constraint

FOREIGN KEY (Yabancı Anahtar) constraint, iki tablo arasındaki bağlantıyı kurar ve referans bütünlüğünü sağlar. Bir tablodaki yabancı anahtar, başka bir tablodaki (veya aynı tablodaki) birincil anahtara referans verir. Bu, yalnızca ilişkili tabloda mevcut olan değerlerin girilebileceği anlamına gelir.

CREATE TABLE Departmanlar (
    DepartmanID INT PRIMARY KEY,
    DepartmanAdi VARCHAR(50) NOT NULL
);

CREATE TABLE Personel (
    PersonelID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50),
    DepartmanID INT,
    FOREIGN KEY (DepartmanID) REFERENCES Departmanlar(DepartmanID)
);

Yabancı anahtarlar, ilişkili ana tablo üzerindeki silme (ON DELETE) ve güncelleme (ON UPDATE) işlemlerinin davranışını da kontrol edebilir:

  • ON DELETE CASCADE: Ana tablodan bir satır silindiğinde, ilgili yabancı anahtar içeren satırları da siler.
  • ON DELETE SET NULL: Ana tablodan bir satır silindiğinde, ilgili yabancı anahtar sütununu NULL yapar.
  • ON DELETE RESTRICT / NO ACTION: İlgili yabancı anahtar içeren satırlar varsa ana tablodan silme işlemine izin vermez. (Varsayılan davranış genellikle RESTRICT’tir.)

5. CHECK Constraint

CHECK constraint, bir sütundaki tüm değerlerin belirli bir koşulu karşılamasını garanti eder. Bu koşul, bir mantıksal ifade olarak tanımlanır ve her veri ekleme veya güncelleme işleminde kontrol edilir. Alan bütünlüğünü ve kullanıcı tanımlı bütünlüğü sağlamak için çok esnek bir yol sunar.

CREATE TABLE Ogrenciler (
    OgrenciID INT PRIMARY KEY,
    Ad VARCHAR(50) NOT NULL,
    Yas INT CHECK (Yas >= 18),
    NotOrtalamasi DECIMAL(3, 2) CHECK (NotOrtalamasi BETWEEN 0.00 AND 4.00),
    Cinsiyet CHAR(1) CHECK (Cinsiyet IN ('E', 'K'))
);

6. DEFAULT Tanımı

DEFAULT tanımı, bir sütuna hiçbir değer belirtilmediğinde otomatik olarak atanacak varsayılan bir değer belirler. Teknik olarak bir kısıtlama olmasa da, alan bütünlüğünü destekler ve veri girişi sırasında eksik değerlerin tutarlı bir şekilde yönetilmesini sağlar.

CREATE TABLE Ayarlar (
    AyarID INT PRIMARY KEY,
    AyarAdi VARCHAR(50) NOT NULL,
    AyarDegeri VARCHAR(100) DEFAULT 'Varsayılan Değer',
    OlusturmaTarihi DATETIME DEFAULT GETDATE() -- SQL Server için
);

Diğer veritabanlarında NOW() veya CURRENT_TIMESTAMP gibi fonksiyonlar kullanılabilir.

Constraint’lerin Yönetimi

Constraint’ler, tablo oluşturulurken tanımlanabileceği gibi, daha sonra ALTER TABLE komutu kullanılarak da eklenebilir veya silinebilir.

Constraint Ekleme:

ALTER TABLE Musteriler
ADD CONSTRAINT UQ_Email UNIQUE (Email);

ALTER TABLE Siparisler
ADD CONSTRAINT FK_MusteriSiparis FOREIGN KEY (MusteriID) REFERENCES Musteriler(MusteriID);

ALTER TABLE Urunler
ADD CONSTRAINT CK_FiyatPozitif CHECK (Fiyat > 0);

Constraint Silme:

Constraint silmek için, genellikle kısıtlamanın adını bilmek gerekir. Kısıtlama adları, tablo oluşturulurken veya ALTER TABLE ADD CONSTRAINT ile açıkça belirtilebilir. Belirtilmezse, veritabanı sistemi otomatik olarak bir ad atar.

ALTER TABLE Musteriler
DROP CONSTRAINT UQ_Email;

ALTER TABLE Personel
DROP CONSTRAINT FK_DepartmanID; -- Veritabanına göre otomatik atanan ad farklı olabilir

SQL Constraint’ler, veritabanlarında veri bütünlüğünü sağlamanın ve korumanın vazgeçilmez bir parçasıdır. NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY ve CHECK gibi kısıtlamalar, verilerin doğruluğunu ve tutarlılığını garantilerken, DEFAULT tanımları da eksik değerlerin tutarlı yönetimine yardımcı olur. Bu kısıtlamaları doğru bir şekilde uygulamak, hatalı veri girişlerini önler, uygulama geliştirme süreçlerini basitleştirir ve uzun vadede veritabanı performansını ve güvenilirliğini artırır. Güçlü bir veritabanı şeması, temelinde sağlam constraint tanımları yatar.


SQL’de Index ve Performans İyileştirme

Veritabanı performansı, modern uygulamaların hızı ve kullanıcı deneyimi için kritik bir faktördür. Veri hacmi arttıkça, veritabanı sorgularının yavaşlaması kaçınılmaz bir sorun haline gelir. SQL indeksleri, bu yavaşlamayı ortadan kaldırmak ve veri erişimini hızlandırmak için temel bir araçtır. Bu makalede, SQL indekslerinin ne olduğunu, nasıl çalıştıklarını, farklı türlerini ve performans iyileştirme stratejilerini derinlemesine inceleyeceğiz.

SQL İndeks Nedir ve Neden Önemlidir?

SQL indeksi, bir kitap dizinine benzer. Bir kitapta belirli bir konuyu bulmak için tüm sayfaları tek tek taramak yerine, dizine bakarak ilgili sayfa numarasına doğrudan gidersiniz. SQL veritabanlarında da indeksler, bir tablodaki belirli verilere çok daha hızlı erişim sağlamak için kullanılır. Tüm tabloyu taramak (tam tablo taraması – full table scan) yerine, veritabanı yönetim sistemi (DBMS), indeks yapısını kullanarak istenen verinin fiziksel konumuna doğrudan ulaşır.

Bu hızlandırma, özellikle büyük veri kümeleri üzerinde çalışan uygulamalar için hayati öneme sahiptir. İndeksler, SELECT sorgularının performansını artırırken, WHERE, JOIN, ORDER BY ve GROUP BY gibi koşulların işlem süresini önemli ölçüde azaltır. Ancak, indekslerin kendilerine özgü bir depolama alanı maliyeti vardır ve INSERT, UPDATE, DELETE gibi veri değiştirme (DML) işlemlerinde ek bir yük oluştururlar, çünkü her DML işleminde indekslerin de güncellenmesi gerekir. Bu nedenle, indeksleme stratejisi dikkatli bir şekilde planlanmalıdır.

İndeks Türleri

SQL veritabanlarında yaygın olarak kullanılan iki ana indeks türü vardır: Clustered Index ve Non-Clustered Index.

Clustered Index (Kümelenmiş İndeks)

Clustered index, bir tablodaki veri satırlarının fiziksel depolama sırasını belirler. Bir tabloya yalnızca bir adet clustered index eklenebilir, çünkü veriler fiziksel olarak sadece bir şekilde sıralanabilir. Genellikle, tablonun birincil anahtarı (Primary Key) otomatik olarak clustered index olarak tanımlanır. Clustered index oluşturulduğunda, tablonun tamamı indeksin sıralamasına göre yeniden düzenlenir. Bu, özellikle veri aralıkları üzerinde yapılan sorgular (WHERE id BETWEEN 100 AND 200) veya sıralama gerektiren sorgular (ORDER BY id) için mükemmel performans sağlar. Verilere doğrudan erişildiği için, “key lookup” ihtiyacı yoktur.

Non-Clustered Index (Kümelenmemiş İndeks)

Non-clustered index, verilerin fiziksel depolama sırasını değiştirmez. Bunun yerine, ayrı bir yapı oluşturur ve bu yapı, indekslenen sütun değerlerini ve bu değerlere karşılık gelen veri satırlarının fiziksel adreslerini (veya clustered index anahtarını) içerir. Bir tabloda birden fazla non-clustered index oluşturulabilir. Bir non-clustered index, bir telefon rehberine benzer; isimlere göre sıralanmıştır ancak numaraların fiziksel konumuyla ilgili değildir, sadece hangi sayfada olduğunu gösterir. Non-clustered indexler, belirli sütunlarda yapılan WHERE koşulları veya JOIN işlemleri için idealdir. Sorgu, indeksi kullanarak istenen veri satırlarına daha hızlı ulaşır.

Covering Indexes (Kapsayan İndeksler)

Bir non-clustered indeks, yalnızca indekslenen sütunları değil, aynı zamanda sorguda ihtiyaç duyulan diğer sütunları da içeriyorsa, buna covering index denir. Yani, sorgu için gerekli tüm veriler sadece indeksin kendisinden okunabilir ve tablonun ana veri sayfalarına gitmeye gerek kalmaz. Bu, ek disk I/O’sunu ortadan kaldırarak performansı önemli ölçüde artırır. Örneğin, bir indeks email sütunu üzerinde oluşturulmuş ve ad ile soyad sütunlarını “included columns” olarak içeriyorsa, SELECT ad, soyad FROM Users WHERE email = '...' sorgusu için index zaten tüm bilgiyi barındırdığı için ana tabloya uğramaz.

İndeksleme Stratejileri ve Ne Zaman İndeks Kullanmalıyız?

Etkili bir indeksleme stratejisi geliştirmek, veritabanı performansını optimize etmenin anahtarıdır. İşte indeks oluşturmayı düşünmeniz gereken durumlar ve stratejiler:

  • `WHERE` Koşullarında Sık Kullanılan Sütunlar: Sorguların `WHERE` ifadesinde sıklıkla filtrelenen sütunlara indeks eklemek, veritabanının arama alanını daraltmasına yardımcı olur.
  • `JOIN` Koşullarında Kullanılan Sütunlar: Tablolar arası ilişkilerde (`JOIN` clause) kullanılan sütunlara indeks eklemek, birleştirme işlemlerini hızlandırır. Genellikle foreign key sütunları bu kategoriye girer.
  • `ORDER BY`, `GROUP BY` ve `DISTINCT` İçin Kullanılan Sütunlar: Verilerin sıralanması veya gruplanması gereken durumlarda ilgili sütunlara indeks eklemek, ek bir sıralama maliyetini (sort operation) ortadan kaldırabilir.
  • Yüksek Kardinaliteye Sahip Sütunlar: Bir sütunun kardinalitesi, o sütundaki benzersiz değerlerin sayısını ifade eder. Yüksek kardinaliteye sahip sütunlar (örn: e-posta adresi, TC kimlik numarası), indeksleme için daha iyi adaydır çünkü indeks, sorguyu çok daha spesifik bir veri alt kümesine yönlendirebilir. Düşük kardinaliteye sahip sütunlar (örn: cinsiyet, doğru/yanlış), indekslendiğinde daha az etki yaratır.
  • Küçük Tablolardan Kaçının: Çok az satıra sahip tablolarda indeksleme genellikle gereksizdir ve hatta performansı kötüleştirebilir, çünkü veritabanı için indeks yapısını okumak, tüm tabloyu taramaktan daha maliyetli olabilir.
  • Sıkça Güncellenmeyen Sütunlar: İndeksler, her veri değişikliğinde güncellenmeleri gerektiği için DML işlemlerine (INSERT, UPDATE, DELETE) ek yük bindirir. Bu nedenle, sıkça güncellenen sütunlara indeks eklerken dikkatli olunmalıdır. En iyi performans, sıkça okunan ancak nadiren yazılan sütunlarda elde edilir.
  • Execution Plan Analizi: Her zaman yavaş çalışan sorguların “Execution Plan”ını analiz edin. Bu planlar, sorgunun nasıl çalıştığını, hangi indeksleri kullandığını veya hangi operasyonların maliyetli olduğunu gösterir. Bu analiz, eksik veya yanlış indeksleri tespit etmenin en güvenilir yoludur.

İndekslemeyi Aşırıya Kaçmaktan Sakınmak

Her sütuna indeks eklemek cazip görünse de, bu genellikle performansı düşüren bir hatadır. Aşırı indeksleme şu sorunlara yol açar:

  • Artan Disk Alanı Kullanımı: Her indeks, disk üzerinde ek yer kaplar.
  • DML İşlemlerinde Performans Düşüşü: Yeni veri eklendiğinde, güncellendiğinde veya silindiğinde, veritabanının ilgili tüm indeksleri de güncellemesi gerekir. Çok sayıda indeks varsa, bu işlemler önemli ölçüde yavaşlayabilir.
  • Optimizatörün Karmaşıklığı: Çok sayıda indeks, veritabanı sorgu optimizatörünün en iyi yürütme planını seçmesini zorlaştırabilir ve hatta yanlış bir plan seçmesine neden olabilir.

Doğru dengeyi bulmak için, uygulamanızın tipik iş yükünü (okuma yoğun mu, yazma yoğun mu?) anlamak ve sorgularınızı sürekli olarak izlemek önemlidir.

İndeks Dışında Performans İyileştirme Yöntemleri

İndeksleme, performans iyileştirmenin önemli bir parçası olsa da, tek çözüm değildir. Bir veritabanının genel performansını artırmak için bütünsel bir yaklaşım benimsemek gerekir:

  • Sorgu Optimizasyonu:
    • `SELECT *` yerine sadece gerekli sütunları seçin.
    • Karmaşık `JOIN`’leri basitleştirin veya alt sorgular yerine uygun `JOIN` türlerini kullanın.
    • `WHERE` koşullarında fonksiyon kullanımından kaçının (`WHERE YEAR(tarih) = 2023` yerine `WHERE tarih BETWEEN ‘2023-01-01’ AND ‘2023-12-31’`). Fonksiyonlar indeks kullanımını engeller.
    • `LIKE ‘%keyword’` gibi baştan joker karakter içeren aramalar indeksleri kullanamaz; mümkünse `LIKE ‘keyword%’` veya full-text search kullanın.
    • Gereksiz `DISTINCT` veya `ORDER BY` kullanımlarından kaçının.
  • Veritabanı Şeması Optimizasyonu:
    • Doğru veri tiplerini kullanın (örn: `INT` yerine gereksiz yere `BIGINT` kullanmaktan kaçının).
    • Normalizasyon ve denormalizasyon kararlarını iş yüküne göre verin.
    • Büyük tablolar için bölümleme (Partitioning) kullanmayı düşünün.
  • Donanım İyileştirmeleri: Daha hızlı CPU’lar, daha fazla RAM ve özellikle hızlı diskler (SSD’ler), veritabanı performansını doğrudan etkiler.
  • Veritabanı Ayarları: Veritabanı yönetim sisteminin (SQL Server, MySQL, PostgreSQL vb.) konfigürasyon ayarları (örn: bellek önbelleği boyutları, bağlantı havuzları) dikkatlice optimize edilmelidir.
  • Saklı Prosedürler ve Fonksiyonlar: Sık kullanılan karmaşık sorguları saklı prosedürler veya fonksiyonlar halinde derlemek, önbelleğe alınmış yürütme planları sayesinde performansı artırabilir.

İndeksler, SQL veritabanı performansını artırmanın temel taşıdır ancak dikkatli bir planlama gerektirir. Doğru indeksleme stratejisi, sorgu hızlarını önemli ölçüde iyileştirirken, aşırı indeksleme DML işlemlerini yavaşlatabilir ve kaynak tüketimini artırabilir. En iyi performansı elde etmek için indekslemeyi, sorgu optimizasyonu, veritabanı şeması tasarımı ve donanım iyileştirmeleri gibi diğer performans teknikleriyle birleştirmek önemlidir. Sürekli izleme ve analiz, veritabanınızın zaman içinde değişen ihtiyaçlarına göre optimum performansını sürdürmenin anahtarıdır.


© 2002 kiziltas.com - Kamil KIZILTAŞ. Her hakkı saklıdır.