Author Archive

SQL’de Transaction ve Veri Tutarlılığı

Veritabanı yönetim sistemlerinin temel taşlarından biri olan transactionlar, SQL dünyasında veri bütünlüğünü ve güvenilirliğini sağlayan kritik mekanizmalardır. Karmaşık işlemleri mantıksal bir birim altında toplayarak, olası hatalara veya sistem kesintilerine karşı veritabanının tutarlı bir durumda kalmasını garanti ederler. Bu sayede, aynı anda birden fazla kullanıcının veritabanı üzerinde güvenle işlem yapabilmesi mümkün olur ve kritik iş süreçleri kesintisiz devam eder.

Transaction (İşlem) Nedir?

Veritabanı bağlamında bir transaction, mantıksal olarak tek bir iş birimi olarak kabul edilen bir dizi SQL ifadesidir. Bu ifadelerin tümü ya başarılı bir şekilde tamamlanır (commit edilir) ya da hiçbiri tamamlanmaz (rollback edilir). Transactionların temel amacı, çok adımlı bir işlem sırasında veritabanının her zaman tutarlı bir durumda kalmasını sağlamaktır. Örneğin, bir banka uygulamasında bir hesaptan başka bir hesaba para transferi işlemi, hem kaynak hesaptan paranın çekilmesi hem de hedef hesaba paranın yatırılması adımlarını içerir. Bu iki adımın da birlikte başarılı olması veya herhangi bir sorun durumunda her iki adımın da geri alınması gerekliliği, transactionların önemini vurgular.

Veri Tutarlılığı ve ACID Özellikleri

Veri tutarlılığı, bir veritabanındaki verilerin belirli kurallar ve kısıtlamalar dahilinde doğru ve güncel kalması durumudur. SQL transactionları, bu tutarlılığı sağlamak için ACID adı verilen dört temel özelliğe uymak zorundadır:

1. Atomicity (Bütünlük)

Atomicity, bir transaction’daki tüm işlemlerin tek bir bütün olarak kabul edildiği anlamına gelir. Yani, bir transaction ya tamamen başarıyla tamamlanır (commit) ya da hiçbir etkisi kalmayacak şekilde tamamen geri alınır (rollback). Kısmi bir tamamlanma söz konusu değildir. Yukarıdaki banka transferi örneğinde, para bir hesaptan çekilip diğerine yatırılmadan önce sistem çökerse, Atomicity özelliği sayesinde işlem tamamen geri alınır ve her iki hesap da önceki durumlarına döner. SQL’de `BEGIN TRANSACTION` ile başlayan ve `COMMIT` veya `ROLLBACK` ile biten bloklar Atomicity prensibini uygular.


BEGIN TRANSACTION;

UPDATE Hesaplar SET Bakiye = Bakiye - 100 WHERE HesapNo = 'TR123';
-- Eğer burada bir hata olursa veya sistem çökerse
UPDATE Hesaplar SET Bakiye = Bakiye + 100 WHERE HesapNo = 'TR456';

COMMIT; -- Eğer her iki işlem de başarılı ise değişiklikler kalıcı olur.
-- ROLLBACK; -- Eğer herhangi bir hata olursa tüm işlemler geri alınır.

2. Consistency (Tutarlılık)

Consistency, bir transaction’ın veritabanını bir tutarlı durumdan alıp başka bir tutarlı duruma bırakması gerektiğini belirtir. Yani, transaction’ın başlangıcında ve sonunda veritabanında tanımlanan tüm kurallar, kısıtlamalar (primary key, foreign key, unique, check kısıtlamaları) ve tetikleyiciler geçerli olmalıdır. Örneğin, bir kullanıcının bakiyesi asla eksi bir değere düşmemelidir kuralı varsa, bir para çekme işlemi bu kuralı ihlal edemez. Consistency, Atomicity ve Isolation ile birlikte çalışarak veritabanının doğru ve güvenilir kalmasını sağlar.

3. Isolation (İzolasyon)

Isolation, eş zamanlı olarak çalışan transactionların birbirini etkilememesi gerektiği anlamına gelir. Bir transaction sanki veritabanında tek başına çalışıyormuş gibi görünmelidir. Başka bir deyişle, bir transaction devam ederken yaptığı değişiklikler, commit edilmeden önce diğer transactionlar tarafından görülemez. Bu, özellikle yüksek eş zamanlılık gerektiren sistemlerde veri tutarsızlıklarını (kirli okuma, tekrarlanamayan okuma, hayalet okuma gibi) önlemek için kritik öneme sahiptir. SQL Server, MySQL gibi veritabanları farklı izolasyon seviyeleri sunarak geliştiricilere esneklik sağlar.


-- Transaction A
BEGIN TRANSACTION;
UPDATE Urunler SET Stok = Stok - 1 WHERE UrunID = 1;
-- Transaction A henüz commit etmedi.

-- Transaction B (Aynı anda çalışıyor)
SELECT Stok FROM Urunler WHERE UrunID = 1; -- Bu sorgu, Transaction A'nın yaptığı değişikliği görmemelidir.

4. Durability (Kalıcılık)

Durability, bir transaction başarılı bir şekilde commit edildikten sonra, yapılan değişikliklerin kalıcı olacağını garanti eder. Sistem çökmesi, güç kesintisi veya başka bir donanım/yazılım hatası bile bu değişiklikleri ortadan kaldıramaz. Veritabanı sistemi, commit edilen verileri kalıcı depolama birimine (sabit disk) yazarak veya günlük kayıtları (transaction log) tutarak bu özelliği sağlar. Sistem yeniden başlatıldığında, commit edilmiş tüm işlemlerin uygulandığı bir durum restore edilir.

Transaction Kontrol Komutları (TCL)

SQL’de transactionları yönetmek için kullanılan temel komutlar şunlardır:

  • BEGIN TRANSACTION veya START TRANSACTION: Yeni bir transaction başlatır.
  • COMMIT: Transaction içindeki tüm değişiklikleri kalıcı hale getirir ve transaction’ı sonlandırır.
  • ROLLBACK: Transaction içindeki tüm değişiklikleri geri alır ve veritabanını transaction’ın başladığı duruma döndürür.
  • SAVEPOINT (isteğe bağlı): Bir transaction içinde belirli bir noktayı işaretlemek için kullanılır. Daha sonra bu noktaya geri dönülebilir.

Neden Transaction Kullanmalıyız?

Transactionlar, veritabanı uygulamaları için vazgeçilmezdir çünkü:

  • Veri Bütünlüğü: ACID özellikleri sayesinde verilerin her zaman doğru ve tutarlı kalmasını sağlarlar.
  • Hata Yönetimi: İşlemler sırasında oluşan hatalarda kolayca önceki duruma dönülmesini (rollback) mümkün kılarak veri bozulmasını önlerler.
  • Eş Zamanlılık Kontrolü: Birden fazla kullanıcının aynı verilere aynı anda erişmesi durumunda tutarsızlıkları önler.
  • İş Süreçleri Güvenliği: Kritik iş süreçlerinin (para transferi, envanter güncelleme, sipariş işleme vb.) güvenilir bir şekilde yürütülmesini garanti ederler.

SQL’de transactionlar ve ACID özellikleri, veritabanı yönetim sistemlerinin güvenilirliğini ve veri bütünlüğünü sağlayan temel yapı taşlarıdır. Atomicity, Consistency, Isolation ve Durability prensipleri sayesinde, karmaşık operasyonlar bile güvenle yürütülür ve sistem hatalarına karşı koruma sağlanır. Bu mekanizmalar, modern veritabanı uygulamalarında verinin doğru, tutarlı ve erişilebilir kalmasının vazgeçilmez bir garantisidir, böylece iş süreçlerinin kesintisiz ve hatasız ilerlemesi mümkün olur.


SQL’de Performans ve Optimizasyon Temelleri

Veritabanı tabanlı uygulamaların temelini oluşturan SQL sorguları, performansı doğrudan etkileyen kritik unsurlardır. Yavaş çalışan sorgular, kullanıcı deneyimini olumsuz etkilemekle kalmaz, aynı zamanda sunucu kaynaklarını da gereksiz yere tüketir. Bu nedenle SQL performans ve optimizasyon temellerini anlamak, herhangi bir yazılım geliştiricisi veya veritabanı yöneticisi için vazgeçilmez bir beceridir. Bu makale, sorgularınızı hızlandırmak ve sistem verimliliğini artırmak için gerekli temel stratejileri derinlemesine inceleyecektir.

Performans Neden Bu Kadar Önemlidir?

SQL sorgularının performansı, bir uygulamanın genel yanıt verme hızını ve kullanıcı memnuniyetini doğrudan etkiler. Yavaş sorgular, sadece kullanıcıların bekleme süresini artırmakla kalmaz, aynı zamanda sunucu üzerinde gereksiz CPU, bellek ve disk G/Ç (I/O) yükü oluşturarak sistem kaynaklarının verimsiz kullanılmasına yol açar. Bu durum, uygulamanın ölçeklenebilirliğini sınırlar, maliyetleri artırır ve hatta iş operasyonlarında aksaklıklara neden olabilir. Özellikle büyük veri kümeleriyle çalışan veya anlık raporlama gerektiren sistemlerde, milisaniyeler bile büyük fark yaratabilir.

Performans Darboğazlarını Belirleme

Optimizasyon sürecinin ilk adımı, performans sorunlarına neden olan sorguları ve alanları doğru bir şekilde tespit etmektir. Bunun için çeşitli araçlar ve yöntemler bulunmaktadır:

Veritabanı İzleme Araçları

Modern veritabanı yönetim sistemleri (DBMS), çalışan sorguları, kilitlenmeleri, bellek ve disk kullanımını izlemek için güçlü araçlar sunar. SQL Server’ın Activity Monitor’ü, MySQL’in Performance Schema’sı veya PostgreSQL’in pg_stat_activity görünümü, o anki veritabanı durumunu ve uzun süren sorguları görmek için kullanılabilir.

EXPLAIN (Execution Plan) Kullanımı

EXPLAIN komutu (veya eşdeğeri), bir SQL sorgusunun veritabanı tarafından nasıl yürütüleceğini gösteren bir “yürütme planı” sağlar. Bu plan, sorgu planlayıcının hangi indeksleri kullandığını, hangi tabloları hangi sırada taradığını veya birleştirdiğini (JOIN) ve tahmini maliyetini detaylandırır. Örneğin, bir sorgunun tam tablo taraması (full table scan) yaptığını görmek, potansiyel bir indeksleme sorununa işaret eder.

EXPLAIN SELECT Ad, Soyad FROM Musteriler WHERE Sehir = 'Ankara' AND Yas > 30;

Bu çıktıyı analiz ederek, sorgunun performansını düşüren adımları (örneğin, indeks kullanılmaması) belirleyebilirsiniz.

Yavaş Sorgu Günlükleri (Slow Query Logs)

Birçok veritabanı sistemi, belirli bir süreden daha uzun süren sorguları otomatik olarak bir günlüğe kaydetme özelliğine sahiptir. Bu günlükler, sürekli olarak en yavaş sorguları tespit etmek ve optimizasyon çalışmaları için birincil hedef belirlemek açısından oldukça değerlidir.

Etkili İndeksleme Stratejileri

İndeksler, veritabanı tablolarındaki verilere hızlı erişim sağlayan özel arama tablolarıdır, tıpkı bir kitabın sonundaki dizin gibi. Doğru kullanıldığında, sorgu performansını katlanarak artırabilirler.

İndeks Türleri

  • Kümelenmiş (Clustered) İndeks: Tablo verilerinin fiziksel depolama sırasını belirler. Bir tabloda yalnızca bir kümelenmiş indeks olabilir ve genellikle birincil anahtar (Primary Key) üzerinde oluşturulur. Veri doğrudan indeks yapısında depolanır.
  • Kümelenmemiş (Non-Clustered) İndeks: Verilerin fiziksel sırasından bağımsızdır. Ayrı bir yapı olarak saklanır ve verinin fiziksel konumuna işaret eder. Bir tabloda birden fazla kümelenmemiş indeks olabilir. Sorgular, indeks üzerinden hızlıca ilgili verilere ulaşır.
  • Bileşik (Composite) İndeks: Birden fazla sütun üzerinde oluşturulan indekslerdir. WHERE, ORDER BY veya GROUP BY yan tümcelerinde sıkça birlikte kullanılan sütunlar için idealdir. Sütunların sırası önemlidir. Örneğin, (Sehir, PostaKodu) indeksi, WHERE Sehir = 'X' veya WHERE Sehir = 'X' AND PostaKodu = 'Y' sorgularında kullanılabilir, ancak sadece WHERE PostaKodu = 'Y' için yeterince etkili olmayabilir.

İndeks Tasarımı İçin En İyi Uygulamalar

  • Sık Kullanılan Sütunlar: WHERE, JOIN ON, ORDER BY ve GROUP BY yan tümcelerinde sıkça kullanılan sütunlara indeks ekleyin.
  • Kardinalite (Cardinality): Yüksek kardinaliteli (benzersiz veya çok sayıda farklı değeri olan) sütunlarda indeksler daha etkili çalışır (örneğin, e-posta adresleri, TC kimlik numaraları). Düşük kardinaliteli (örneğin, cinsiyet, durum gibi az sayıda farklı değeri olan) sütunlarda indeksler genellikle daha az faydalıdır veya maliyeti faydasından ağır basabilir.
  • Gereksiz İndekslerden Kaçınma: Her indeks, veri ekleme, güncelleme ve silme (INSERT, UPDATE, DELETE) işlemlerinin maliyetini artırır çünkü verinin yanı sıra indeksin de güncellenmesi gerekir. Bu nedenle sadece gerçekten gerekli olan indeksleri oluşturun.
  • İndeks Boyutu: İndeks ne kadar küçük olursa, belleğe sığma olasılığı o kadar yüksek olur ve bu da daha hızlı erişim anlamına gelir. Mümkünse kısa ve uygun veri tipleri kullanın.

Sorgu Optimizasyon Teknikleri

İndeksleme kadar önemli olan bir diğer konu da sorguların kendisinin doğru ve etkili bir şekilde yazılmasıdır.

SELECT Deyimi Optimizasyonu

  • Yıldız Kullanımından Kaçınma (SELECT *): Sadece ihtiyaç duyulan sütunları seçin. Gereksiz sütunların alınması, hem ağ trafiğini artırır hem de veritabanı sunucusunun daha fazla veri okumasına neden olur.

    -- Kötü Uygulama
    SELECT * FROM Urunler;
    
    -- İyi Uygulama
    SELECT UrunAdi, Fiyat FROM Urunler;

WHERE Koşulları Optimizasyonu

  • SARGable (Search Argument Able) Koşullar: İndekslerin kullanılabilmesi için WHERE koşullarının SARGable olması gerekir. Bu, koşulun doğrudan bir sütunun değerine uygulanması ve sütun üzerinde fonksiyon kullanılmamasının tercih edilmesi anlamına gelir.

    -- Kötü Uygulama (İndeks kullanamayabilir)
    SELECT * FROM Siparisler WHERE YEAR(SiparisTarihi) = 2023;
    
    -- İyi Uygulama (İndeks kullanabilir)
    SELECT * FROM Siparisler WHERE SiparisTarihi BETWEEN '2023-01-01' AND '2023-12-31';
  • LIKE Operatörü: Wildcard karakteri (%) koşulun başında kullanıldığında indeksler genellikle kullanılamaz.

    -- İndeks kullanabilir
    SELECT * FROM Musteriler WHERE Ad LIKE 'A%';
    
    -- İndeks kullanamaz
    SELECT * FROM Musteriler WHERE Ad LIKE '%A%';
  • OR Koşulları: Bazı durumlarda, OR içeren karmaşık koşullar yerine UNION ALL kullanarak ayrı sorguları birleştirmek daha performanslı olabilir. Ancak bu, veritabanı optimizasyoncusunun yeteneğine ve sorgunun yapısına bağlıdır.

JOIN Optimizasyonu

  • Doğru JOIN Türünü Seçmek: INNER JOIN, LEFT JOIN, RIGHT JOIN gibi farklı JOIN türlerinin ne zaman kullanılacağını iyi anlamak önemlidir.
  • ON Clause’unda İndeksli Sütunlar: Birleştirme koşulunda kullanılan sütunlar üzerinde indeks bulunması JOIN işlemlerini hızlandırır.
  • Küçük Tabloyu Önce Birleştirmek: Bazı veritabanları, JOIN sıralamasını optimize edebilir ancak genel kural olarak, en küçük sonuç kümesini üreten tabloyu diğerleriyle birleştirmek daha verimli olabilir.

Alt Sorgular (Subqueries) ve EXISTS/IN

Çoğu durumda, alt sorgular yerine JOIN kullanmak daha performanslıdır çünkü JOIN’ler veritabanı motoru tarafından daha etkili bir şekilde optimize edilebilir. EXISTS operatörü, IN operatörüne göre genellikle daha verimlidir, özellikle alt sorgu büyük sonuç kümeleri döndürüyorsa. EXISTS, koşulu sağlayan ilk satırı bulduğunda taramayı durdurabilirken, IN tüm alt sorguyu çalıştırmak zorunda kalabilir.

-- IN Kullanımı
SELECT Ad FROM Musteriler WHERE MusteriID IN (SELECT MusteriID FROM Siparisler WHERE ToplamTutar > 1000);

-- EXISTS Kullanımı (Genellikle daha performanslı)
SELECT M.Ad FROM Musteriler M WHERE EXISTS (SELECT 1 FROM Siparisler S WHERE S.MusteriID = M.MusteriID AND S.ToplamTutar > 1000);

GROUP BY ve ORDER BY Optimizasyonu

Bu yan tümcelerde kullanılan sütunlarda indeks bulunması, sıralama veya gruplama işlemlerini hızlandırabilir. Büyük veri kümelerinde gereksiz sıralamalardan kaçınmak veya sonuçları mümkün olduğunca erken filtrelemek önemlidir.

LIMIT/TOP Kullanımı

Bir sorgunun döndürdüğü sonuç sayısını sınırlamak (örneğin, bir sayfalama işlemi için) ağ trafiğini ve bellek kullanımını önemli ölçüde azaltır. LIMIT (MySQL, PostgreSQL) veya TOP (SQL Server) komutları bu amaçla kullanılır.

SELECT UrunAdi, Fiyat FROM Urunler ORDER BY Fiyat DESC LIMIT 10;

Veritabanı Şema Tasarımı

Veritabanının temel yapısı, SQL sorgularının performansı üzerinde derin bir etkiye sahiptir.

Normalizasyon ve Denormalizasyon

  • Normalizasyon: Veri tekrarını en aza indirme ve veri tutarlılığını sağlama sürecidir. Genellikle daha fazla tablo ve JOIN işlemi gerektirdiğinden okuma performansını bir miktar düşürebilir.
  • Denormalizasyon: Performansı artırmak için kasıtlı olarak veri tekrarına izin verme veya tabloları birleştirme işlemidir. Özellikle yoğun okuma, az yazma olan raporlama veya analitik sistemlerde faydalı olabilir. Ancak veri tutarlılığını sağlamak için ek çaba gerektirir.

Doğru Veri Türleri

Her sütun için en uygun ve en küçük veri türünü seçmek, depolama alanını azaltır, disk G/Ç’yi düşürür ve indekslerin daha verimli çalışmasına olanak tanır. Örneğin, bir sayının INT yerine SMALLINT veya TINYINT olarak saklanması, hem bellekte hem de diskte daha az yer kaplar.

Referans Bütünlüğü (Foreign Keys)

Yabancı anahtarlar (Foreign Keys), tablolar arasındaki ilişkileri ve veri bütünlüğünü sağlar. Ayrıca, veritabanı sorgu planlayıcısına tablolar arası ilişkiler hakkında ipuçları vererek daha iyi optimizasyon kararları almasına yardımcı olabilir.

Sunucu ve Veritabanı Yapılandırması

Yazılımsal optimizasyonlar kadar, donanım ve veritabanı sunucusunun doğru yapılandırılması da kritik öneme sahiptir.

Donanım Kaynakları

  • CPU: Özellikle karmaşık sorgular ve paralel işlemler için yeterli işlem gücü.
  • Bellek (RAM): Veritabanı önbellekleri (buffer pool, shared buffers) için kritik. Daha fazla bellek, disk G/Ç’yi azaltarak verilere daha hızlı erişim sağlar.
  • Disk G/Ç (I/O): Veritabanı, büyük miktarda veri okuma ve yazma işlemi yapar. SSD’ler, geleneksel HDD’lere göre önemli ölçüde daha iyi G/Ç performansı sunarak veritabanı hızını artırır. RAID konfigürasyonları da performansı ve güvenilirliği etkiler.

Veritabanı Ayarları

Her veritabanı yönetim sisteminin performansı etkileyen kendine özgü yapılandırma parametreleri vardır. Bunlar arasında bellek önbellek boyutları, işlem günlüğü (transaction log) ayarları, bağlantı havuzu limitleri ve paralel sorgu yürütme limitleri bulunur. Bu ayarlar, sistemin donanım kapasitesine ve iş yüküne göre dikkatlice ayarlanmalıdır.

Düzenli Bakım ve İzleme

Performans optimizasyonu, tek seferlik bir görev değil, sürekli bir süreçtir.

İndeks Bakımı

Veritabanında sürekli veri ekleme, güncelleme ve silme işlemleri, indekslerin parçalanmasına (fragmentation) neden olabilir. Parçalanmış indeksler, veritabanının verilere erişimini yavaşlatır. İndeksleri düzenli olarak yeniden oluşturmak (REBUILD) veya yeniden düzenlemek (REORGANIZE), performanslarını korumak için önemlidir.

İstatistik Güncellemeleri

Veritabanı sorgu planlayıcısı, en iyi yürütme planını oluşturmak için tablolardaki verilerin dağılımı hakkındaki istatistiklere güvenir. Bu istatistikler güncel olmadığında, planlayıcı yanlış kararlar alabilir. Otomatik istatistik güncelleme mekanizmaları olsa da, büyük veri değişikliklerinden sonra veya periyodik olarak manuel olarak güncellemeler yapmak (örneğin, ANALYZE TABLE) faydalıdır.

Periyodik Performans Analizi

Veritabanı performansını düzenli olarak izlemek, yavaşlayan sorguları veya sistemdeki yeni darboğazları erken tespit etmeyi sağlar. Bu, proaktif bir yaklaşımla olası sorunların büyümeden çözülmesine yardımcı olur.

SQL performans optimizasyonu, tek seferlik bir işlemden ziyade sürekli bir çabadır. Doğru indeksleme, etkili sorgu yazımı, uygun veritabanı şeması tasarımı ve düzenli izleme, sistemlerinizin sürekli yüksek performansla çalışmasını sağlar. Bu temel prensipleri benimseyerek, veritabanı uygulamalarınızın hem hızlı hem de ölçeklenebilir olmasını garanti edebilirsiniz. Unutmayın, iyi optimize edilmiş bir veritabanı, daha iyi kullanıcı deneyimi ve daha düşük operasyonel maliyet anlamına gelir.


SQL’de Görünümler (Stored Procedure ve Trigger Temelleri)

SQL veritabanı yönetiminde, verimlilik, güvenlik ve veri bütünlüğü kritik öneme sahiptir. Bu bağlamda, Görünümler (Views), Saklı Yordamlar (Stored Procedures) ve Tetikleyiciler (Triggers) gibi yapısal elemanlar, geliştiricilere güçlü araçlar sunar. Bu makale, söz konusu bu temel SQL bileşenlerinin ne işe yaradığını, nasıl kullanıldığını ve veritabanı işlemlerini nasıl optimize ettiğini ayrıntılarıyla inceleyecektir.

SQL Görünümler (Views)

Görünümler, SQL’de fiziksel olarak veri depolamayan, ancak bir veya daha fazla temel tablonun verilerinden türetilmiş sanal tablolardır. Bir görünüm aslında kaydedilmiş bir sorgudan ibarettir ve her çağrıldığında bu sorgu çalıştırılır. Görünümler, kullanıcılar için veri erişimini basitleştirir ve veri soyutlama katmanı oluşturur.

Görünümlerin Kullanım Amaçları

  • Güvenlik ve Veri Gizliliği: Kullanıcılara tüm tabloyu göstermek yerine, sadece yetkili oldukları belirli sütun veya satırları içeren görünümler sunularak hassas veriler gizlenebilir. Bu, yetkilendirme katmanını veritabanı seviyesine taşır.
  • Sorgu Basitliği ve Veri Soyutlama: Karmaşık JOIN’ler ve filtrelemeler içeren uzun sorguları bir görünüm içinde saklayarak, son kullanıcıların bu karmaşıklığı anlamadan sadece basit SELECT * FROM view_adı; gibi ifadelerle verilere erişmesini sağlar.
  • Veri Bütünlüğü ve Tutarlılık: Farklı tablolardan birleştirilmiş veriyi tutarlı bir yapı altında sunarak, raporlama veya analiz süreçlerini kolaylaştırır.
  • Uyumluluk: Temel tablo yapısı değişse bile (örneğin sütun adları), görünüm tanımı güncellenerek uygulama katmanının etkilenmemesi sağlanabilir.

Görünüm Oluşturma ve Kullanımı

Bir görünüm, CREATE VIEW ifadesiyle oluşturulur. Örneğin, müşteri ve sipariş bilgilerini birleştiren bir görünüm:

CREATE VIEW MusteriSiparisDetay AS
SELECT
    m.MusteriID,
    m.Ad,
    m.Soyad,
    s.SiparisID,
    s.SiparisTarihi,
    s.ToplamTutar
FROM
    Musteriler m
JOIN
    Siparisler s ON m.MusteriID = s.MusteriID
WHERE
    s.SiparisTarihi > '2023-01-01';

Bu görünümü kullanmak tıpkı bir tablo kullanmak gibidir:

SELECT MusteriID, Ad, Soyad, SiparisTarihi FROM MusteriSiparisDetay WHERE ToplamTutar > 1000;

Mevcut bir görünümü değiştirmek için ALTER VIEW, silmek için ise DROP VIEW kullanılır.

Saklı Yordamlar (Stored Procedures)

Saklı yordamlar, bir veya daha fazla SQL ifadesinden oluşan, önceden derlenmiş ve veritabanında saklanan programatik bloklardır. Bu yordamlar, parametre alabilir, kontrol akışı ifadeleri (IF, WHILE) içerebilir ve geri değer döndürebilirler.

Saklı Yordamların Kullanım Amaçları

  • Performans İyileştirme: Saklı yordamlar ilk çağrıldığında derlenir ve veritabanı sunucusunda önbelleğe alınır. Sonraki çağrılarda tekrar derlenmeye gerek kalmadığından daha hızlı çalışır. Ayrıca, ağ trafiğini azaltır çünkü uygulama sadece yordam adını ve parametrelerini gönderir, tüm SQL kodunu değil.
  • Güvenlik: Kullanıcılara doğrudan tablolara erişim vermek yerine, belirli işlemleri gerçekleştiren saklı yordamlara erişim izni verilerek daha güvenli bir ortam sağlanır. Bu, veri manipülasyonunu ve yetkilendirmeyi merkezi hale getirir.
  • Modülerlik ve Yeniden Kullanılabilirlik: Sık kullanılan veya karmaşık iş mantığına sahip işlemleri tek bir yordamda toplayarak kodun tekrar yazılmasını önler ve bakım kolaylığı sağlar.
  • Veri Bütünlüğü ve Tutarlılık: Veritabanı seviyesinde iş kurallarını uygulayarak, veri girişi veya güncellemelerinde tutarlılığı garanti eder.

Saklı Yordam Oluşturma ve Kullanımı

Bir saklı yordam CREATE PROCEDURE ifadesiyle tanımlanır. Aşağıdaki örnek, yeni bir müşteri ekleyen bir yordamı gösterir:

CREATE PROCEDURE YeniMusteriEkle
    @Ad NVARCHAR(50),
    @Soyad NVARCHAR(50),
    @Email NVARCHAR(100)
AS
BEGIN
    INSERT INTO Musteriler (Ad, Soyad, Email)
    VALUES (@Ad, @Soyad, @Email);

    SELECT SCOPE_IDENTITY() AS YeniMusteriID; -- Eklenen müşterinin ID'sini döndürür
END;

Bu yordam EXEC veya EXECUTE komutuyla çağrılır:

EXEC YeniMusteriEkle 'Ayşe', 'Yılmaz', 'ayse.yilmaz@example.com';

Saklı yordamlar OUT parametreleri aracılığıyla da değer döndürebilir, bu da daha karmaşık işlevsellik sunar.

Tetikleyiciler (Triggers)

Tetikleyiciler, belirli bir tabloda INSERT, UPDATE veya DELETE gibi bir olay meydana geldiğinde otomatik olarak çalışan özel saklı yordam türleridir. Tetikleyiciler, veritabanı seviyesinde iş kurallarını zorlamak, denetim (auditing) kayıtları tutmak veya basamaklı değişiklikler yapmak için kullanılır.

Tetikleyicilerin Kullanım Amaçları

  • Otomatik İşlemler: Bir veri değişikliği olduğunda otomatik olarak başka bir işlemi tetikleyerek veritabanı düzeyinde iş mantığı uygular.
  • Denetim (Auditing): Bir tablodaki veri değişikliklerini (kimin, ne zaman, neyi değiştirdiğini) bir denetim tablosuna kaydetmek için idealdir.
  • Veri Doğrulama: Bir kayıt eklenmeden veya güncellenmeden önce verinin belirli kurallara uygunluğunu kontrol etmek ve gerekirse işlemi iptal etmek için kullanılabilir (BEFORE tetikleyiciler).
  • Basamaklı Değişiklikler (Cascading Changes): Bir tabloda yapılan değişikliğin, ilgili diğer tablolara otomatik olarak yansımasını sağlamak (örneğin, bir kullanıcının silinmesiyle ilişkili tüm yorumlarını da silmek).

Tetikleyici Türleri ve Oluşturma

Tetikleyiciler, olayın zamanlamasına göre farklı türlerde olabilir:

  • AFTER Tetikleyiciler: İlgili SQL olayı (INSERT, UPDATE, DELETE) gerçekleştikten sonra tetiklenir. Çoğunlukla denetim veya başka bir tabloda veri güncellemek için kullanılır.
  • BEFORE Tetikleyiciler: İlgili SQL olayı gerçekleşmeden önce tetiklenir (SQL Server’da INSTEAD OF tetikleyiciler bu senaryoların bir kısmını karşılar, diğer veritabanlarında BEFORE bulunur). Genellikle veri doğrulama ve veri üzerinde değişiklik yapma için kullanılır.
  • INSTEAD OF Tetikleyiciler: Olayın kendisi yerine tetiklenir. Genellikle doğrudan güncellenemeyen görünümleri (JOIN içeren görünümler gibi) güncellemek için kullanılır.

Aşağıdaki örnek, bir ürünün fiyatı güncellendiğinde, eski ve yeni fiyatı bir log tablosuna kaydeden bir AFTER UPDATE tetikleyiciyi gösterir:

CREATE TRIGGER trg_UrunFiyatGuncellemeLog
ON Urunler
AFTER UPDATE
AS
BEGIN
    IF UPDATE(Fiyat) -- Sadece Fiyat sütunu güncellendiğinde çalışır
    BEGIN
        INSERT INTO UrunFiyatLog (UrunID, EskiFiyat, YeniFiyat, GuncellemeTarihi, GuncelleyenKullanici)
        SELECT
            d.UrunID,
            d.Fiyat AS EskiFiyat,
            i.Fiyat AS YeniFiyat,
            GETDATE(),
            SUSER_SNAME() -- Güncelleyen kullanıcı adını alır
        FROM
            DELETED d
        INNER JOIN
            INSERTED i ON d.UrunID = i.UrunID;
    END;
END;

INSERTED ve DELETED sanal tabloları, tetikleyici içinde ilgili olayın eski ve yeni değerlerine erişmek için kullanılır. INSERTED tablosu yeni eklenen veya güncellenen satırları, DELETED tablosu ise silinen veya güncellenmeden önceki satırları içerir.

SQL’deki Görünümler, Saklı Yordamlar ve Tetikleyiciler, modern veritabanı yönetiminin temel taşlarıdır. Veri güvenliğini artırırken, karmaşık sorguları basitleştirir, performansı optimize eder ve iş mantığını veritabanı seviyesinde uygularlar. Bu araçları etkin bir şekilde kullanarak, geliştiriciler daha sağlam, bakımı kolay ve performanslı veritabanı uygulamaları oluşturabilir, böylece veri bütünlüğünü ve sistem verimliliğini garantileyebilirler.


SQL’de Fonksiyonlar (Functions)

SQL veritabanlarında veri manipülasyonu ve sorgulama işlemleri sırasında karşılaşılan karmaşık görevleri basitleştirmek için fonksiyonlar güçlü bir araçtır. Bu yapısal elemanlar, belirli bir işlevi yerine getiren önceden tanımlanmış veya kullanıcı tarafından oluşturulmuş kod bloklarıdır. Fonksiyonlar, sorguları daha okunabilir hale getirirken, kod tekrarını azaltır ve verimliliği artırır. Veritabanı yönetiminde etkin bir şekilde kullanıldığında, SQL fonksiyonları geliştirme süreçlerini hızlandırır ve veri işleme mantığını merkezi bir yerde toplar.

SQL Fonksiyon Türleri

SQL fonksiyonları temel olarak iki ana kategoriye ayrılır: Yerleşik (Built-in) Fonksiyonlar ve Kullanıcı Tanımlı (User-Defined) Fonksiyonlar (UDF).

Yerleşik (Built-in) Fonksiyonlar

Veritabanı sistemleri (örneğin SQL Server, MySQL, PostgreSQL, Oracle) tarafından doğrudan sağlanan ve kullanıma hazır fonksiyonlardır. Bu fonksiyonlar genellikle çeşitli veri tipleri üzerinde işlemler yapmak için tasarlanmıştır ve geniş bir yelpazede görevleri kapsar.

Metin (String) Fonksiyonları

Metinsel ifadeler üzerinde işlem yapmak için kullanılırlar.

  • LEN() veya LENGTH(): Bir metin dizisinin karakter sayısını döndürür.
    SELECT LEN('Merhaba Dünya'); -- Sonuç: 13
  • CONCAT(): Birden fazla metin dizisini birleştirir.
    SELECT CONCAT('Merhaba', ' ', 'Dünya'); -- Sonuç: Merhaba Dünya
  • UPPER() ve LOWER(): Metni sırasıyla büyük veya küçük harfe dönüştürür.
    SELECT UPPER('sql dersleri'); -- Sonuç: SQL DERSLERI
  • SUBSTRING() veya SUBSTR(): Bir metin dizisinin belirli bir bölümünü alır.
    SELECT SUBSTRING('Veritabanı', 1, 5); -- Sonuç: Verit
  • REPLACE(): Bir metin dizisindeki belirli bir alt diziyi başka bir alt diziyle değiştirir.
    SELECT REPLACE('Merhaba Dünya', 'Dünya', 'Evren'); -- Sonuç: Merhaba Evren

Sayısal (Numeric) Fonksiyonlar

Sayısal değerler üzerinde matematiksel işlemler yapmak için kullanılırlar.

  • ROUND(): Bir sayıyı belirtilen ondalık basamak sayısına yuvarlar.
    SELECT ROUND(123.456, 2); -- Sonuç: 123.46
  • FLOOR(): Bir sayıyı kendisinden küçük en büyük tam sayıya yuvarlar (aşağı yuvarlama).
    SELECT FLOOR(123.99); -- Sonuç: 123
  • CEIL() veya CEILING(): Bir sayıyı kendisinden büyük en küçük tam sayıya yuvarlar (yukarı yuvarlama).
    SELECT CEILING(123.01); -- Sonuç: 124
  • ABS(): Bir sayının mutlak değerini döndürür.
    SELECT ABS(-100); -- Sonuç: 100
  • SQRT(): Bir sayının karekökünü döndürür.
    SELECT SQRT(81); -- Sonuç: 9

Tarih ve Saat (Date and Time) Fonksiyonları

Tarih ve saat değerleri üzerinde işlem yapmak için kullanılırlar.

  • GETDATE() veya NOW(): Mevcut tarih ve saati döndürür.
    SELECT GETDATE(); -- Sonuç: 2023-10-27 10:30:45.123 (örnek)
  • DATEDIFF(): İki tarih arasındaki farkı belirtilen birim cinsinden döndürür.
    SELECT DATEDIFF(day, '2023-01-01', '2023-01-31'); -- Sonuç: 30
  • DATEADD(): Bir tarihe belirtilen birim cinsinden bir değer ekler.
    SELECT DATEADD(month, 1, '2023-01-15'); -- Sonuç: 2023-02-15 00:00:00.000
  • YEAR(), MONTH(), DAY(): Bir tarihin yıl, ay veya gün bileşenini döndürür.
    SELECT YEAR('2023-10-27'); -- Sonuç: 2023

Aggregate (Toplama) Fonksiyonları

Bir sütundaki birden fazla satırdaki veriyi özetleyen tek bir değer döndürmek için kullanılırlar. Genellikle GROUP BY ifadesiyle birlikte kullanılırlar.

  • COUNT(): Bir sütundaki (NULL olmayan) satır sayısını veya tüm satırların sayısını döndürür.
    SELECT COUNT(*) FROM Musteriler;
  • SUM(): Bir sayısal sütundaki tüm değerlerin toplamını döndürür.
    SELECT SUM(SatisMiktari) FROM Satislar;
  • AVG(): Bir sayısal sütundaki tüm değerlerin ortalamasını döndürür.
    SELECT AVG(Fiyat) FROM Urunler;
  • MIN() ve MAX(): Bir sütundaki en küçük ve en büyük değeri döndürür.
    SELECT MIN(StokAdedi), MAX(StokAdedi) FROM Stoklar;

Dönüşüm (Conversion) Fonksiyonları

Bir veri tipini başka bir veri tipine dönüştürmek için kullanılırlar.

  • CAST() ve CONVERT(): Bir ifadeyi veya sütunu belirli bir veri tipine dönüştürür.
    SELECT CAST('123' AS INT); -- Sonuç: 123 (integer)
    SELECT CONVERT(VARCHAR(10), GETDATE(), 103); -- Sonuç: 27/10/2023 (belirli formatta)

Kullanıcı Tanımlı (User-Defined) Fonksiyonlar (UDF’ler)

Yerleşik fonksiyonların yeterli olmadığı veya özel iş mantığının gerektiği durumlarda, geliştiriciler kendi fonksiyonlarını oluşturabilirler. UDF’ler, belirli bir mantığı kapsüllemek ve SQL sorgularında tekrar kullanılabilirliği sağlamak için tasarlanmıştır.

Scalar Fonksiyonlar

Tek bir değer döndüren fonksiyonlardır. Genellikle karmaşık hesaplamalar veya veri manipülasyonları için kullanılırlar.

CREATE FUNCTION dbo.CalculateTax (@price DECIMAL(10, 2), @taxRate DECIMAL(5, 2))
RETURNS DECIMAL(10, 2)
AS
BEGIN
    RETURN @price * @taxRate;
END;

-- Kullanım Örneği
SELECT dbo.CalculateTax(100.00, 0.18); -- Sonuç: 18.00

Tablo Değerli Fonksiyonlar (Table-Valued Functions – TVF’ler)

Sonuç olarak bir tablo döndüren fonksiyonlardır. Bu fonksiyonlar, karmaşık sorguları basitleştirmek ve view’ler gibi kullanılmak üzere tasarlanmıştır.

  • Inline TVF’ler: Tek bir SELECT ifadesinden oluşan ve view’lere benzer şekilde çalışan TVF’lerdir. Performans açısından genellikle tercih edilirler.
  • Multi-statement TVF’ler: Birden fazla T-SQL ifadesi içerebilen daha karmaşık TVF’lerdir. İçlerinde değişken tanımlayabilir, döngüler ve koşullu ifadeler kullanabilirler. Ancak performans açısından inline TVF’lere göre daha maliyetli olabilirler.
-- Inline Table-Valued Function Örneği
CREATE FUNCTION dbo.GetProductsByCategory (@categoryName NVARCHAR(50))
RETURNS TABLE
AS
RETURN
(
    SELECT ProductID, ProductName, Price
    FROM Products
    WHERE Category = @categoryName
);

-- Kullanım Örneği
SELECT ProductID, ProductName FROM dbo.GetProductsByCategory('Elektronik');

SQL Fonksiyonlarının Avantajları

  • Kod Tekrarını Azaltma: Tekrar eden iş mantığını bir fonksiyon içinde kapsülleyerek kod tekrarını önler ve bakım maliyetlerini düşürür.
  • Modülerlik ve Okunabilirlik: Sorguları daha modüler ve okunabilir hale getirir. Karmaşık hesaplamalar fonksiyonlara taşınarak ana sorgu basitleştirilir.
  • Performans İyileştirmeleri: Özellikle yerleşik fonksiyonlar ve iyi tasarlanmış inline TVF’ler sorgu performansını artırabilir. Veritabanı motorları bu fonksiyonları optimize edilmiş bir şekilde çalıştırır.
  • Veri Bütünlüğü ve Güvenlik: Hassas veri işlemleri veya iş kuralları fonksiyonlar aracılığıyla uygulanabilir, bu da veri bütünlüğünü ve güvenliğini artırır. Ayrıca kullanıcıların doğrudan tablolara erişimini kısıtlayıp sadece fonksiyonlar üzerinden işlem yapmalarına izin verilebilir.

Fonksiyon Kullanımına Dikkat Edilmesi Gerekenler

  • Performans Etkisi: Özellikle kullanıcı tanımlı fonksiyonlar (UDF’ler), sorguların performansını olumsuz etkileyebilir. Özellikle scalar UDF’ler her satır için ayrı ayrı çağrıldığında ciddi yavaşlamalara neden olabilir. Mümkün olduğunda yerleşik fonksiyonlar veya inline TVF’ler tercih edilmelidir.
  • Yan Etkiler (Side Effects): SQL Server’da UDF’ler, veritabanının durumunu değiştiren (INSERT, UPDATE, DELETE gibi DML işlemleri yapan) yan etkilere sahip olamazlar. Bu durum, fonksiyonların veri okuma amaçlı olmasını ve deterministik bir sonuç üretmesini sağlar.
  • Veritabanı Motoru Bağımlılığı: Bazı yerleşik fonksiyonlar veritabanı motoruna özgü olabilir. Bu, farklı veritabanı sistemleri arasında geçiş yaparken uyumluluk sorunlarına yol açabilir.
  • Karmaşıklık: Çok karmaşık UDF’ler oluşturmak, hata ayıklamayı zorlaştırabilir ve sistemin anlaşılırlığını azaltabilir. Fonksiyonlar genellikle tek bir görevi yerine getirecek şekilde basit tutulmalıdır.

SQL fonksiyonları, veritabanı işlemlerini optimize eden, kod tekrarını önleyen ve sorguların okunabilirliğini artıran vazgeçilmez araçlardır. Yerleşik ve kullanıcı tanımlı fonksiyonlar sayesinde, veri işleme mantığı modüler bir yapıda toplanabilir, geliştirme süreçleri hızlanabilir. Ancak, performans üzerindeki potansiyel etkileri göz önünde bulundurularak dikkatli kullanılmaları önemlidir. Fonksiyonları doğru anlamak ve etkin bir şekilde uygulamak, SQL geliştiricileri için veritabanı yönetiminde büyük faydalar sağlar ve daha sürdürülebilir sistemler inşa etmeye olanak tanır.


SQL’de Tablo İlişkileri ve JOIN Kavramı

Veritabanı yönetimi söz konusu olduğunda, verilerin düzenli, tutarlı ve erişilebilir olması kritik öneme sahiptir. İlişkisel veritabanları, bu amacı gerçekleştirmek için tablolar arası bağlantı mekanizmalarını kullanır. Bu bağlantılar, farklı veri parçacıklarını mantıksal olarak bir araya getirerek anlamlı bilgi kümeleri oluşturmamızı sağlar. SQL’deki JOIN kavramı ise, bu ilişkilerden faydalanarak birden fazla tablodaki verileri tek bir sorguda birleştirmemize olanak tanıyan temel bir araçtır.

SQL’de Tablo İlişkileri

İlişkisel veritabanı tasarımının kalbinde tablo ilişkileri yatar. Bu ilişkiler, veritabanını daha verimli hale getirmek, veri tekrarını (redüntasyon) azaltmak ve veri bütünlüğünü sağlamak amacıyla kullanılır. Birincil Anahtar (Primary Key – PK) ve Yabancı Anahtar (Foreign Key – FK) kavramları, bu ilişkilerin kurulmasında temel yapı taşlarıdır.

Birincil Anahtar (Primary Key – PK)

Bir tablodaki her satırı benzersiz bir şekilde tanımlayan bir veya daha fazla sütundur. Birincil anahtar null (boş) değer içeremez ve her zaman benzersiz olmalıdır. Örneğin, bir “Müşteriler” tablosundaki “MüşteriID” sütunu bir birincil anahtar olabilir.

Yabancı Anahtar (Foreign Key – FK)

Başka bir tablodaki birincil anahtara referans veren bir veya daha fazla sütundur. Yabancı anahtar, iki tablo arasında bir bağlantı kurar ve referans bütünlüğünü sağlar. Örneğin, “Siparişler” tablosundaki “MüşteriID” sütunu, “Müşteriler” tablosundaki “MüşteriID” birincil anahtarına referans veren bir yabancı anahtardır. Bu sayede, var olmayan bir müşteriye sipariş girilmesi engellenir.

İlişki Türleri

Tablolar arasındaki ilişkiler genellikle üç ana kategoriye ayrılır:

1. Bire Bir İlişki (One-to-One – 1:1)

Bir tablodaki her kaydın, diğer tablodaki yalnızca bir kayıtla eşleştiği ilişki türüdür. Bu tür ilişkiler genellikle, bir tablonun çok fazla sütunu olduğunda ve bazı sütunların sık kullanılmadığı durumlarda, veri bütünlüğünü veya güvenliğini artırmak amacıyla kullanılır. Örneğin, “Kullanıcılar” tablosu ile “KullanıcıDetayları” tablosu arasında bir 1:1 ilişki olabilir. Her kullanıcı sadece bir detay kaydına, her detay kaydı ise sadece bir kullanıcıya ait olabilir.


CREATE TABLE Kullanicilar (
    KullaniciID INT PRIMARY KEY,
    KullaniciAdi VARCHAR(50)
);

CREATE TABLE KullaniciDetaylari (
    KullaniciID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50),
    Email VARCHAR(100),
    FOREIGN KEY (KullaniciID) REFERENCES Kullanicilar(KullaniciID)
);

2. Bire Çok İlişki (One-to-Many – 1:N)

En yaygın ilişki türüdür. Bir tablodaki bir kaydın, diğer tablodaki birden çok kayıtla eşleştiği, ancak diğer tablodaki her kaydın ilk tablodaki yalnızca bir kayıtla eşleştiği ilişkidir. Örneğin, bir “Departmanlar” tablosundaki her departman, “Çalışanlar” tablosundaki birden çok çalışana sahip olabilir, ancak her çalışan yalnızca bir departmana aittir.


CREATE TABLE Departmanlar (
    DepartmanID INT PRIMARY KEY,
    DepartmanAdi VARCHAR(100)
);

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

3. Çoka Çok İlişki (Many-to-Many – N:N)

Bir tablodaki bir kaydın, diğer tablodaki birden çok kayıtla ve diğer tablodaki bir kaydın da ilk tablodaki birden çok kayıtla eşleştiği ilişki türüdür. Doğrudan iki tablo arasında kurulamaz; bu tür bir ilişkiyi yönetmek için genellikle bir ara (bağlantı/köprü) tablo kullanılır. Örneğin, “Öğrenciler” tablosu ile “Dersler” tablosu arasında çoka çok ilişki vardır. Bir öğrenci birden çok ders alabilir ve bir ders birden çok öğrenciye verilebilir. Bu ilişkiyi “ÖğrenciDersleri” adında bir ara tablo ile çözeriz.


CREATE TABLE Ogrenciler (
    OgrenciID INT PRIMARY KEY,
    OgrenciAdi VARCHAR(50)
);

CREATE TABLE Dersler (
    DersID INT PRIMARY KEY,
    DersAdi VARCHAR(100)
);

-- Ara tablo
CREATE TABLE OgrenciDersleri (
    OgrenciID INT,
    DersID INT,
    PRIMARY KEY (OgrenciID, DersID),
    FOREIGN KEY (OgrenciID) REFERENCES Ogrenciler(OgrenciID),
    FOREIGN KEY (DersID) REFERENCES Dersler(DersID)
);

SQL’de JOIN Kavramı

SQL’deki JOIN ifadesi, ilişkili sütunlardaki değerlere dayanarak iki veya daha fazla tablonun satırlarını birleştirmek için kullanılır. Bu, farklı tablolarda depolanan verileri tek bir sonuç kümesinde görmemizi sağlar.

JOIN Türleri

Farklı senaryolara uygun çeşitli JOIN türleri bulunur:

1. INNER JOIN (İç Birleştirme)

İki tablonun birleşim koşulunu karşılayan (yani her iki tabloda da eşleşen değeri olan) satırları döndürür. Eşleşme olmayan satırlar sonuç kümesine dahil edilmez.


SELECT Calisanlar.Ad, Calisanlar.Soyad, Departmanlar.DepartmanAdi
FROM Calisanlar
INNER JOIN Departmanlar ON Calisanlar.DepartmanID = Departmanlar.DepartmanID;

2. LEFT JOIN (LEFT OUTER JOIN) (Sol Dış Birleştirme)

Sol tablodaki (FROM anahtar kelimesinden sonra belirtilen ilk tablo) tüm satırları ve sağ tablodaki eşleşen satırları döndürür. Eğer sağ tabloda bir eşleşme bulunamazsa, sol tablodaki satırlar yine de döndürülür ve sağ tabloya ait sütunlar için NULL değerleri görüntülenir.


SELECT Calisanlar.Ad, Calisanlar.Soyad, Departmanlar.DepartmanAdi
FROM Calisanlar
LEFT JOIN Departmanlar ON Calisanlar.DepartmanID = Departmanlar.DepartmanID;

Bu sorgu, tüm çalışanları listeler; bir departmanı olmayan çalışanlar için `DepartmanAdi` sütunu NULL olacaktır.

3. RIGHT JOIN (RIGHT OUTER JOIN) (Sağ Dış Birleştirme)

Sağ tablodaki (JOIN anahtar kelimesinden sonra belirtilen tablo) tüm satırları ve sol tablodaki eşleşen satırları döndürür. Eğer sol tabloda bir eşleşme bulunamazsa, sağ tablodaki satırlar yine de döndürülür ve sol tabloya ait sütunlar için NULL değerleri görüntülenir.


SELECT Calisanlar.Ad, Calisanlar.Soyad, Departmanlar.DepartmanAdi
FROM Calisanlar
RIGHT JOIN Departmanlar ON Calisanlar.DepartmanID = Departmanlar.DepartmanID;

Bu sorgu, tüm departmanları listeler; bir çalışanı olmayan departmanlar için `Ad` ve `Soyad` sütunları NULL olacaktır. (LEFT JOIN ile tabloların yerini değiştirerek aynı sonucu elde etmek mümkündür).

4. FULL JOIN (FULL OUTER JOIN) (Tam Dış Birleştirme)

Her iki tablodaki tüm satırları döndürür. Bir tabloda eşleşme bulunamazsa, diğer tablonun sütunları için NULL değerleri görüntülenir. Hem LEFT JOIN hem de RIGHT JOIN’in birleşimi olarak düşünülebilir.


SELECT Calisanlar.Ad, Calisanlar.Soyad, Departmanlar.DepartmanAdi
FROM Calisanlar
FULL JOIN Departmanlar ON Calisanlar.DepartmanID = Departmanlar.DepartmanID;

Bu sorgu, departmanı olmayan çalışanları, çalışanı olmayan departmanları ve eşleşen tüm çalışan-departman kombinasyonlarını listeler.

5. CROSS JOIN (Çapraz Birleştirme)

Her iki tablonun tüm olası kombinasyonlarını (Kartezyen çarpımını) döndürür. Yani, ilk tablodaki her satır, ikinci tablodaki her satırla birleştirilir. Genellikle dikkatli kullanılmalıdır çünkü büyük tablolarda çok fazla sonuç üretebilir.


SELECT Calisanlar.Ad, Departmanlar.DepartmanAdi
FROM Calisanlar
CROSS JOIN Departmanlar;

6. SELF JOIN (Kendine Birleştirme)

Bir tabloyu kendi kendine birleştirmektir. Aynı tablo içinde hiyerarşik verileri sorgulamak veya bir tablodaki satırları kendi içindeki diğer satırlarla karşılaştırmak için kullanılır. Bu, genellikle tabloya takma ad (alias) verilerek yapılır.


SELECT
    C1.Ad AS CalisanAdi,
    C1.Soyad AS CalisanSoyadi,
    C2.Ad AS YoneticiAdi,
    C2.Soyad AS YoneticiSoyadi
FROM
    Calisanlar C1
INNER JOIN
    Calisanlar C2 ON C1.YoneticiID = C2.CalisanID;

Bu örnekte, `Calisanlar` tablosundaki çalışanları yöneticileriyle birlikte listelemek için kendi kendine birleştirme kullanılır. Her çalışanın bir `YoneticiID` sütunu olduğu varsayılmıştır.

JOIN Koşulları: ON ve USING

  • ON Anahtar Kelimesi: Genellikle birleştirilecek sütunlar farklı adlara sahip olduğunda veya daha karmaşık birleştirme koşulları gerektiğinde kullanılır.
  • USING Anahtar Kelimesi: Birleştirilecek sütunların her iki tabloda da aynı ada sahip olduğu durumlarda daha kısa ve okunaklı bir syntax sağlar.

-- ON kullanımı (genel ve esnek)
SELECT C.Ad, D.DepartmanAdi
FROM Calisanlar C
INNER JOIN Departmanlar D ON C.DepartmanID = D.DepartmanID;

-- USING kullanımı (sütun adları aynı ise)
-- Not: Her SQL veritabanı sistemi USING'i desteklemeyebilir (örn. SQL Server)
SELECT C.Ad, D.DepartmanAdi
FROM Calisanlar C
INNER JOIN Departmanlar D USING (DepartmanID);

Tablo ilişkileri, sağlam ve optimize edilmiş veritabanı tasarımlarının temelini oluştururken, JOIN ifadeleri bu tasarımlardan anlamlı ve birleştirilmiş verileri çekmek için vazgeçilmez bir araçtır. Doğru JOIN türünü seçmek, ihtiyacınız olan veriye etkin bir şekilde ulaşmanızı sağlar.

SQL’de tablo ilişkileri ve JOIN kavramı, ilişkisel veritabanı yönetim sistemlerinin gücünü ortaya koyan temel taşlardır. Doğru tasarlanmış tablo ilişkileri, veri bütünlüğünü sağlarken veri tekrarını en aza indirir. JOIN’ler ise bu ilişkileri kullanarak, farklı tablolara dağılmış verileri mantıksal bir bütünlük içinde bir araya getirip anlamlı sorgu sonuçları elde etmemizi mümkün kılar. Her bir JOIN türünün kendine özgü kullanım senaryoları bulunur ve ihtiyaç duyulan veriye ulaşmak için doğru JOIN tipini seçmek, verimlilik açısından kritik öneme sahiptir.


SQL’de Yedekleme, Güvenlik ve Yetkilendirme

Günümüzün veri odaklı dünyasında, SQL veritabanları birçok kritik uygulamanın bel kemiğini oluşturmaktadır. Bu değerli bilgilerin korunması, iş sürekliliği ve veri bütünlüğü için hayati öneme sahiptir. Veritabanı yönetimi, sadece veri depolamakla kalmaz, aynı zamanda olası felaketlere karşı yedekleme stratejileri geliştirmeyi, kötü niyetli saldırılardan korunmak için güvenlik önlemleri almayı ve kullanıcıların verilere erişimini hassas bir şekilde yetkilendirmeyi de gerektirir. Bu üç temel sütun, sağlam bir veritabanı altyapısının vazgeçilmezidir.

SQL’de Yedekleme: Veri Kaybına Karşı Sigorta

Veri kaybı, donanım arızaları, yazılım hataları, doğal afetler veya insan hatası gibi çeşitli nedenlerle meydana gelebilir. SQL veritabanlarında yedekleme, bu tür durumlar karşısında verilerinizi kurtarmanın tek güvenilir yoludur. Etkili bir yedekleme stratejisi, iş sürekliliğini sağlar ve felaket kurtarma (Disaster Recovery) planlarının temelini oluşturur.

Yedekleme Çeşitleri ve Stratejileri

  • Tam Yedekleme (Full Backup): Bir veritabanının veya belirli dosya gruplarının tümünü içerir. En kapsamlı yedekleme türüdür ancak en uzun süreyi alır ve en çok depolama alanı gerektirir. Felaket anında hızlı bir kurtarma noktası sunar.
  • Diferansiyel Yedekleme (Differential Backup): En son tam yedeklemeden bu yana değişen tüm verileri içerir. Tam yedeklemeye göre daha hızlıdır ve daha az yer kaplar. Geri yükleme için yalnızca en son tam yedeklemeye ve en son diferansiyel yedeklemeye ihtiyaç duyar.
  • İşlem Günlüğü Yedeklemesi (Transaction Log Backup): SQL Server gibi veritabanlarında, veritabanında yapılan tüm değişiklikleri kaydeder. Küçük ve sık yedeklemeler için idealdir, bu da veri kaybını minimuma indirir. Özellikle kritik sistemlerde Point-in-Time Recovery (belirli bir ana geri dönme) olanağı sunar. MySQL gibi sistemlerde benzer işlevselliği binary log’lar (binlog) aracılığıyla elde edebiliriz.

Yedekleme ve Geri Yükleme Süreçleri

Yedeklemeler, belirlenen sıklıkta otomatik olarak çalışacak şekilde planlanmalıdır. Yedeklerin depolanması, ana veritabanı sunucusundan fiziksel olarak ayrı ve güvenli bir konumda (örneğin, ağ depolama, bulut depolama veya farklı bir veri merkezi) yapılmalıdır. Yedekleme kadar önemli olan bir diğer adım ise geri yükleme (restore) işlemlerinin düzenli olarak test edilmesidir. Bir yedeklemenin çalışır durumda olduğundan emin olmak için düzenli aralıklarla test geri yüklemeleri yapılmalı ve kurtarma süreleri ölçülmelidir.

Örnek (SQL Server):

BACKUP DATABASE YourDatabase
TO DISK = 'C:\Backup\YourDatabase_Full.bak'
WITH FORMAT, COMPRESSION, STATS = 10;

RESTORE DATABASE YourDatabase
FROM DISK = 'C:\Backup\YourDatabase_Full.bak'
WITH REPLACE;

SQL’de Güvenlik: Verilerinizi Koruma Kalkanı

Veritabanı güvenliği, hassas verileri yetkisiz erişimden, kötü amaçlı saldırılardan, veri ihlallerinden ve bozulmalardan korumak için uygulanan önlemlerin bütünüdür. Bir SQL veritabanı için güvenlik katmanlı bir yaklaşım gereklidir.

Başlıca Güvenlik Tehditleri ve Önlemler

  • SQL Enjeksiyonu (SQL Injection): Kullanıcı girdileri aracılığıyla kötü amaçlı SQL kodlarının çalıştırılmasına izin veren bir güvenlik açığıdır.
    • Önlem: Parametreli sorgular (parameterized queries) veya hazırlanmış ifadeler (prepared statements) kullanmak. Kullanıcı girdilerini asla doğrudan SQL sorgusuna eklememek. Giriş doğrulama (input validation) yapmak.
  • Zayıf Parolalar ve Kimlik Doğrulama: Kolay tahmin edilebilir parolalar veya yetersiz kimlik doğrulama mekanizmaları, yetkisiz erişim riskini artırır.
    • Önlem: Karmaşık parolalar kullanmak, düzenli parola değiştirmeyi zorunlu kılmak, çok faktörlü kimlik doğrulama (MFA) uygulamak.
  • Yetkisiz Erişim ve Aşırı Yetkiler: Kullanıcılara veya uygulamalara gereğinden fazla yetki verilmesi, güvenlik ihlallerine zemin hazırlar.
    • Önlem: En az ayrıcalık (Least Privilege) ilkesini uygulamak. Her kullanıcının veya uygulamanın yalnızca işini yapmak için ihtiyaç duyduğu minimum yetkiye sahip olmasını sağlamak.
  • Veri Şifreleme (Data Encryption): Verilerin hem depoda (at rest) hem de iletimde (in transit) korunması.
    • Önlem: Veritabanı düzeyinde şeffaf veri şifreleme (TDE – Transparent Data Encryption) kullanmak (SQL Server). İletişimi SSL/TLS kullanarak şifrelemek.
  • Yamalar ve Güncellemeler: Veritabanı yönetim sistemindeki (DBMS) güvenlik açıklarının giderilmemesi.
    • Önlem: DBMS ve işletim sistemini düzenli olarak güncel tutmak, güvenlik yamalarını zamanında uygulamak.
  • Denetim (Auditing): Veritabanı etkinliklerini izlememek.
    • Önlem: Veritabanı denetim mekanizmalarını etkinleştirmek ve şüpheli etkinlikleri takip etmek.

SQL’de Yetkilendirme: Doğru Kişiye Doğru Erişim

Yetkilendirme, bir kullanıcının (veya bir uygulamanın) kimliği doğrulandıktan sonra veritabanında hangi eylemleri gerçekleştirebileceğini ve hangi verilere erişebileceğini belirleme sürecidir. Kimlik doğrulama “kimsiniz?” sorusunu yanıtlarken, yetkilendirme “ne yapabilirsiniz?” sorusunu yanıtlar.

Temel Yetkilendirme Mekanizmaları

  • Kullanıcılar ve Oturumlar (Logins/Users): Veritabanına erişmek için oluşturulan bireysel kimliklerdir. Genellikle bir sunucu oturumu (login) ve veritabanı kullanıcısı (user) olarak ayrılırlar.
  • Roller (Roles): Benzer yetkilere sahip kullanıcıları gruplamak için kullanılan mantıksal yapılardır. Roller, yetki yönetimini basitleştirir ve hata yapma olasılığını azaltır. Örneğin, ‘Veri Okuyucu’ rolü tüm tablolarda SELECT yetkisine sahipken, ‘Veri Yazıcı’ rolü SELECT, INSERT, UPDATE, DELETE yetkilerine sahip olabilir.
  • İzinler/Ayrıcalıklar (Permissions/Privileges): Kullanıcılara veya rollere belirli nesneler (tablolar, görünümler, saklı yordamlar) üzerinde veya sistem genelinde (örneğin, veritabanı oluşturma) verilen yetkilerdir. Başlıca izin komutları şunlardır:
    • GRANT: Bir kullanıcıya veya role izin vermek için kullanılır.
    • REVOKE: Verilmiş bir izni geri almak için kullanılır.
    • DENY: Bir kullanıcının veya rolün belirli bir eylemi gerçekleştirmesini açıkça yasaklar (REVOKE’dan daha güçlüdür ve çakışan GRANT izinlerini geçersiz kılar).

Yetkilendirme İçin En İyi Uygulamalar

  • En Az Ayrıcalık İlkesi: Kullanıcıların ve rollerin yalnızca işlerini yapmak için kesinlikle ihtiyaç duyduğu minimum ayrıcalıklara sahip olmasını sağlamak.
  • Rol Kullanımı: Bireysel kullanıcılara doğrudan izin vermek yerine rolleri kullanarak yetkileri yönetmek. Bu, kullanıcılar katıldığında veya ayrıldığında yönetimi kolaylaştırır.
  • Düzenli Denetim: Mevcut izinleri ve rolleri düzenli olarak gözden geçirmek, gereksiz veya eski yetkileri kaldırmak.
  • Şema Tabanlı Güvenlik: Veritabanı nesnelerini (tablolar, görünümler) şemalar altında düzenleyerek, şema düzeyinde izinler vermek, yönetimi daha modüler hale getirir.
  • Satır Düzeyi Güvenlik (RLS – Row-Level Security): Belirli bir kullanıcının veya rolün, bir tablodaki yalnızca kendiyle ilgili satırlara erişmesini sağlamak. Bu, özellikle çok kiracılı (multi-tenant) uygulamalarda veya hassas verilerin bulunduğu senaryolarda önemlidir.

Örnek (SQL Server):

CREATE LOGIN MyLogin WITH PASSWORD = 'StrongPassword123!';
CREATE USER MyUser FOR LOGIN MyLogin;
CREATE ROLE DataReaderRole;
GRANT SELECT ON SCHEMA::dbo TO DataReaderRole;
ALTER ROLE DataReaderRole ADD MEMBER MyUser;

SQL veritabanlarında yedekleme, güvenlik ve yetkilendirme, modern veri yönetiminin ayrılmaz bileşenleridir. Bu üç alan, veritabanı altyapısının sağlamlığını, verilerin bütünlüğünü ve gizliliğini temin etmek için birbiriyle sıkı bir şekilde ilişkilidir. Etkili yedekleme stratejileriyle olası felaketlere karşı hazırlıklı olmak, katmanlı güvenlik önlemleriyle dış tehditlere karşı bir kalkan oluşturmak ve hassas yetkilendirme mekanizmalarıyla erişimi doğru bir şekilde kontrol etmek, veri kaybını ve ihlallerini önlemenin anahtarıdır. Proaktif bir yaklaşımla bu unsurlara yatırım yapmak, uzun vadeli iş başarısı için kritik öneme sahiptir.


SQL’de Veritabanı İlişkileri ve Normalizasyon

SQL tabanlı veritabanlarında veri bütünlüğünü ve tutarlılığını sağlamanın iki temel taşı, veritabanı ilişkileri ve normalizasyondur. Bu kavramlar, verilerin nasıl depolandığını, birbirleriyle nasıl bağlantı kurduğunu ve gereksiz tekrarların nasıl önlendiğini belirler. Etkili bir veritabanı tasarımı, performansı optimize ederken veri anormalliklerini minimize eder, bu da daha güvenilir ve yönetilebilir sistemler oluşturmanın anahtarıdır. Bu makale, her iki konuyu derinlemesine inceleyecektir.

Veritabanı İlişkilerine Derinlemesine Bakış

Veritabanı ilişkileri, bir ilişkisel veritabanındaki tabloların birbirleriyle nasıl etkileşim kurduğunu tanımlar. Bu ilişkiler, veriler arasındaki mantıksal bağlantıları kurar ve veri bütünlüğünü korumak için hayati öneme sahiptir. Temelde, bir tablonun birincil anahtarı (Primary Key – PK) ile başka bir tablonun yabancı anahtarı (Foreign Key – FK) arasındaki bağlantılar aracılığıyla kurulurlar. Yabancı anahtar, diğer tablodaki birincil anahtara referans vererek iki tablo arasında bir köprü görevi görür.

Bire Bir (One-to-One) İlişki

Bire bir ilişki, bir tablodaki her bir kaydın, ilişkili olduğu diğer tablodaki yalnızca bir kayıtla eşleştiği durumu ifade eder. Bu tür ilişkiler genellikle iki ana nedenden kullanılır: ya çok fazla sütunu olan bir tabloyu bölerek yönetilebilirliği artırmak için ya da hassas verileri ayrı bir tabloda tutarak güvenliği artırmak için. Örneğin, bir Kullanicilar tablosundaki her kullanıcının yalnızca bir KullaniciDetaylari kaydı olabilir. Bu durumda, KullaniciDetaylari tablosunda, Kullanicilar tablosundaki KullaniciID‘yi referans gösteren bir yabancı anahtar bulunur ve bu yabancı anahtara bir benzersiz (UNIQUE) kısıtlaması eklenerek bire bir ilişki sağlanır.


CREATE TABLE Kullanicilar (
    KullaniciID INT PRIMARY KEY,
    KullaniciAdi VARCHAR(50) UNIQUE,
    Eposta VARCHAR(100)
);

CREATE TABLE KullaniciDetaylari (
    DetayID INT PRIMARY KEY,
    KullaniciID INT UNIQUE, -- Bire Bir ilişki için UNIQUE kısıtlaması
    Adres VARCHAR(255),
    Telefon VARCHAR(20),
    FOREIGN KEY (KullaniciID) REFERENCES Kullanicilar(KullaniciID)
);

Bire Çok (One-to-Many) İlişki

Bire çok ilişki, en yaygın veritabanı ilişkisi türüdür. Bu ilişkide, bir tablodaki tek bir kayıt, başka bir tablodaki birden fazla kayıtla ilişkilendirilebilir, ancak diğer tablodaki her bir kayıt, ilk tablodaki yalnızca bir kayıtla ilişkilendirilir. Örneğin, bir Musteriler tablosundaki bir müşteri, Siparisler tablosunda birden fazla siparişe sahip olabilir, ancak her sipariş yalnızca bir müşteriye aittir. Bu ilişki, Siparisler tablosunda Musteriler tablosunun birincil anahtarını (MusteriID) referans gösteren bir yabancı anahtar (MusteriID) tanımlanarak kurulur.


CREATE TABLE Musteriler (
    MusteriID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50)
);

CREATE TABLE Siparisler (
    SiparisID INT PRIMARY KEY,
    MusteriID INT, -- Yabancı anahtar
    SiparisTarihi DATE,
    ToplamTutar DECIMAL(10, 2),
    FOREIGN KEY (MusteriID) REFERENCES Musteriler(MusteriID)
);

Çoka Çok (Many-to-Many) İlişki

Çoka çok ilişki, bir tablodaki birden fazla kaydın, başka bir tablodaki birden fazla kayıtla ilişkilendirilebildiği durumu ifade eder. Bu tür ilişkiler doğrudan iki tablo arasında kurulamaz; bunun yerine, genellikle “ilişki tablosu” veya “birleştirici tablo” (junction table) adı verilen üçüncü bir aracı tablo kullanılarak çözülür. Örneğin, bir öğrenci birden fazla ders alabilir ve bir ders birden fazla öğrenciye sahip olabilir. Bu durumu yönetmek için Ogrenciler, Dersler ve OgrenciDersleri adında bir tabloya ihtiyaç duyulur. OgrenciDersleri tablosu, Ogrenciler tablosundan OgrenciID‘yi ve Dersler tablosundan DersID‘yi yabancı anahtar olarak içerir ve genellikle bu iki anahtarın birleşimi birincil anahtarı oluşturur.


CREATE TABLE Ogrenciler (
    OgrenciID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50)
);

CREATE TABLE Dersler (
    DersID INT PRIMARY KEY,
    DersAdi VARCHAR(100),
    Kredi INT
);

CREATE TABLE OgrenciDersleri (
    OgrenciID INT,
    DersID INT,
    PRIMARY KEY (OgrenciID, DersID), -- Bileşik birincil anahtar
    FOREIGN KEY (OgrenciID) REFERENCES Ogrenciler(OgrenciID),
    FOREIGN KEY (DersID) REFERENCES Dersler(DersID)
);

Veritabanı Normalizasyonu: Temeller ve Formlar

Normalizasyon, bir veritabanı tasarımını, veri yedekliliğini (redundancy) azaltmak ve veri anormalliklerini (anomalies) önlemek amacıyla belirli kurallar veya “normal formlar” çerçevesinde düzenleme sürecidir. Temel amacı, veri bütünlüğünü sağlamak, veritabanı boyutunu optimize etmek ve ekleme, güncelleme ve silme işlemlerinde ortaya çıkabilecek tutarsızlıkları minimize etmektir. Normalizasyon, bir tablonun hangi formda olduğunu belirlemek için fonksiyonel bağımlılıkları inceler.

  • Ekleme Anormalliği (Insertion Anomaly): Yeni bir veri eklerken, ilgisiz veya eksik veri eklemek zorunda kalma.
  • Silme Anormalliği (Deletion Anomaly): Bir veriyi silerken, başka değerli bir verinin de yanlışlıkla silinmesi.
  • Güncelleme Anormalliği (Update Anomaly): Bir veriyi güncellerken, aynı verinin birden fazla yerde tutulması nedeniyle tüm kopyalarını güncellemeyi unutma ve tutarsızlık yaşanması.

Birinci Normal Form (1NF)

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

  1. Her hücrede atomik (bölünemez) değerler bulunmalıdır. Yani, bir hücrede birden fazla değer veya virgülle ayrılmış değerler olmamalıdır.
  2. Her bir sütunun tek bir veri türü olmalı ve tekrarlayan gruplar içermemelidir.
  3. Her bir satır benzersiz olmalıdır (bir birincil anahtar ile tanımlanabilir).

Örnek olarak, bir Calisanlar tablosunda TelefonNumaralari sütunu, bir çalışanın birden fazla telefon numarasına sahip olabileceği ve bunları virgülle ayırarak tuttuğu bir yapı 1NF’ye aykırıdır. Bu durumda, her telefon numarası için ayrı bir satır oluşturulmalı veya telefon numaraları için ayrı bir tablo oluşturulmalıdır.

İkinci Normal Form (2NF)

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

  1. 1NF’de olmalıdır.
  2. Anahtar olmayan (non-key) her öznitelik, birincil anahtarın tamamına fonksiyonel olarak bağlı olmalıdır. Yani, birincil anahtar bileşikse (birden fazla sütundan oluşuyorsa), anahtar olmayan hiçbir öznitelik birincil anahtarın yalnızca bir kısmına bağlı olmamalıdır. Bu durum “kısmi bağımlılık” olarak bilinir.

Örnek: Bir SiparisDetaylari tablosu (SiparisID, UrunID, Miktar, UrunAdi, UrunFiyati) birincil anahtarı (SiparisID, UrunID) olsun. Burada UrunAdi ve UrunFiyati sadece UrunID‘ye bağlıdır, SiparisID‘ye değil. Bu, kısmi bağımlılıktır. 2NF için UrunAdi ve UrunFiyati bilgilerini ayrı bir Urunler tablosuna taşımak gerekir.

Üçüncü Normal Form (3NF)

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

  1. 2NF’de olmalıdır.
  2. Anahtar olmayan hiçbir öznitelik, birincil anahtarın başka bir anahtar olmayan özniteliğine geçişli (transitive) olarak bağlı olmamalıdır. Yani, A -> B ve B -> C bağımlılıkları varsa (burada A birincil anahtardır ve B anahtar olmayan bir özniteliktir), C özniteliği B’ye geçişli olarak bağlıdır.

Örnek: Bir Personel tablosu (PersonelID, Ad, Soyad, DepartmanAdi, DepartmanTelefonu) olsun ve PersonelID birincil anahtar olsun. Burada DepartmanTelefonu, DepartmanAdi‘na bağlıdır ve DepartmanAdi da PersonelID‘ye bağlıdır. Yani PersonelID -> DepartmanAdi -> DepartmanTelefonu bir geçişli bağımlılıktır. 3NF için DepartmanAdi ve DepartmanTelefonu bilgilerini ayrı bir Departmanlar tablosuna taşımak gerekir.

BCNF (Boyce-Codd Normal Form)

BCNF, 3NF’nin daha katı bir versiyonudur. Bir tablo BCNF’de olmak için 3NF’de olmalı ve ek olarak, her determinat (belirleyici), bir aday anahtar olmalıdır. Yani, bir öznitelik veya öznitelik kümesi başka bir öznitelik veya öznitelik kümesini belirliyorsa, bu belirleyici bir aday anahtar olmak zorundadır. Nadir durumlarda 3NF’de olan ancak BCNF’de olmayan tablolar ortaya çıkabilir, genellikle birden fazla çakışan aday anahtarın bulunduğu durumlar için geçerlidir.

İlişkiler ve Normalizasyonun SQL Uygulamaları

SQL, veritabanı ilişkilerini ve normalizasyon prensiplerini uygulamak için çeşitli mekanizmalar sunar. PRIMARY KEY, FOREIGN KEY ve UNIQUE kısıtlamaları bu yapının temelini oluşturur. PRIMARY KEY, bir tablodaki her satırı benzersiz şekilde tanımlarken, FOREIGN KEY bir tablonun başka bir tablodaki birincil anahtara referans vermesini sağlar ve bu sayede referans bütünlüğünü (referential integrity) korur. UNIQUE kısıtlaması ise, belirli bir sütundaki tüm değerlerin benzersiz olmasını garanti eder ve bire bir ilişkilerin kurulmasında rol oynayabilir.

Bu kısıtlamalar sayesinde, SQL veritabanı motoru, tanımlanan ilişkisel kurallara uymayan herhangi bir veri değişikliğini (ekleme, güncelleme, silme) otomatik olarak reddeder. Örneğin, bir Musteriler tablosunda olmayan bir MusteriID ile Siparisler tablosuna kayıt eklemeye çalışmak, bir yabancı anahtar kısıtlaması ihlaline yol açar ve işlem başarısız olur. Bu, veri tutarlılığının sağlanmasında kritik bir rol oynar.

Referans bütünlüğü kısıtlamaları, SQL’de ON DELETE ve ON UPDATE kuralları ile daha da esneklik kazanır:

  • CASCADE: Ana tablodaki kayıt silindiğinde/güncellendiğinde, ilişkili tablodaki tüm ilgili kayıtları da siler/günceller.
  • SET NULL: Ana tablodaki kayıt silindiğinde/güncellendiğinde, ilişkili tablodaki yabancı anahtar sütununu NULL yapar (eğer NULL değerine izin veriyorsa).
  • RESTRICT (varsayılan): İlişkili tablolarda ilgili kayıtlar varken ana tablodaki kaydın silinmesini/güncellenmesini engeller.
  • NO ACTION: RESTRICT’e benzer, ancak standartlar arasında küçük farkları olabilir.

-- Normalizasyon ve ilişkileri gösteren örnek tablo yapıları
CREATE TABLE Departmanlar (
    DepartmanID INT PRIMARY KEY,
    DepartmanAdi VARCHAR(100) UNIQUE,
    Lokasyon VARCHAR(100)
);

CREATE TABLE Calisanlar (
    CalisanID INT PRIMARY KEY,
    Ad VARCHAR(50),
    Soyad VARCHAR(50),
    DepartmanID INT, -- Yabancı anahtar
    Maas DECIMAL(10, 2),
    FOREIGN KEY (DepartmanID) REFERENCES Departmanlar(DepartmanID)
        ON DELETE RESTRICT ON UPDATE CASCADE -- İlişkisel eylemler
);

CREATE TABLE Projeler (
    ProjeID INT PRIMARY KEY,
    ProjeAdi VARCHAR(100),
    Butce DECIMAL(15, 2)
);

CREATE TABLE CalisanProjeleri (
    CalisanID INT,
    ProjeID INT,
    AtanmaTarihi DATE,
    PRIMARY KEY (CalisanID, ProjeID),
    FOREIGN KEY (CalisanID) REFERENCES Calisanlar(CalisanID) ON DELETE CASCADE,
    FOREIGN KEY (ProjeID) REFERENCES Projeler(ProjeID) ON DELETE CASCADE
);

Veritabanı ilişkileri ve normalizasyon, modern SQL veritabanı tasarımının temel taşlarıdır. Doğru ilişkileri kurmak ve veritabanını uygun normal formlara getirmek, veri bütünlüğünü artırır, veri tekrarlarını azaltır ve olası anormallikleri önler. Bu prensipler, daha sağlam, ölçeklenebilir ve yönetilebilir veritabanı sistemleri oluşturarak uygulamanın performansını ve güvenilirliğini doğrudan etkiler. Bu nedenle, her veritabanı geliştiricisinin bu kavramlara hakim olması kritik öneme sahiptir.


SQL’de Görünümler (VIEW) ve Sanal Tablolar

SQL veritabanlarında Görünümler (VIEW), kullanıcıların veya uygulamaların temel tablolardaki verilere daha organize, güvenli ve basitleştirilmiş bir şekilde erişmesini sağlayan güçlü araçlardır. Fiziksel olarak veri depolamazlar ancak bir veya daha fazla tablodan türetilmiş, önceden tanımlanmış bir sorgunun sonucunu sunan sanal tablolar olarak işlev görürler. Bu “sanal tablolar”, veri erişimini soyutlayarak karmaşıklığı azaltır ve güvenlik katmanları ekler.

Görünüm (View) Nedir?

Bir Görünüm, temel (fiziksel) tablolardaki belirli verileri mantıksal olarak temsil eden, ancak kendisi veri depolamayan sanal bir tablodur. Temelde, bir SQL sorgusunun kaydedilmiş halidir. Bir görünüm sorgulandığında, kaydedildiği zaman tanımlanan temel sorgu çalıştırılır ve sonucu tıpkı fiziksel bir tabloymuş gibi döndürülür. Bu sayede, kullanıcılar veya uygulamalar, karmaşık join’ler, filtrelemeler veya hesaplamalar içerebilen bir dizi temel tablo yerine basitçe görünüme sorgu gönderebilirler.

Görünümler Neden Kullanılır?

Görünümlerin kullanımı, veritabanı yönetiminde birçok avantaj sunar:

Veri Güvenliği

Görünümler, hassas verilerin gizlenmesi için etkili bir yöntem sunar. Örneğin, bir personel tablosu maaş veya kişisel bilgileri içerebilir. Bu bilgilere tüm kullanıcıların erişmesi gerekmeyebilir. Bir görünüm oluşturarak, yalnızca belirli sütunlara (örn. Ad, Soyad, Departman) veya belirli satırlara (örn. Yalnızca kendi departmanının çalışanları) erişimi kısıtlayabilirsiniz. Böylece, kullanıcılara doğrudan temel tablolara erişim izni vermek yerine görünümlere erişim izni verilerek güvenlik artırılır.


CREATE VIEW CalisanPublicBilgiler AS
SELECT CalisanID, Ad, Soyad, Departman
FROM Calisanlar;

Karmaşık Sorguları Basitleştirme

Birden fazla tablodan veri birleştiren, filtreleyen veya hesaplamalar içeren karmaşık SQL sorguları, görünümler aracılığıyla basitleştirilebilir. Geliştiriciler veya son kullanıcılar, bu karmaşık mantığı her seferinde yeniden yazmak zorunda kalmadan, önceden tanımlanmış görünümleri basit bir SELECT deyimiyle sorgulayabilirler. Bu, sorgu geliştirme süresini azaltır ve hataları minimize eder.


CREATE VIEW MusteriSiparisOzeti AS
SELECT
    M.MusteriAd,
    S.SiparisTarihi,
    SUM(SD.Miktar * U.BirimFiyat) AS ToplamTutar
FROM
    Musteriler M
JOIN
    Siparisler S ON M.MusteriID = S.MusteriID
JOIN
    SiparisDetaylari SD ON S.SiparisID = SD.SiparisID
JOIN
    Urunler U ON SD.UrunID = U.UrunID
GROUP BY
    M.MusteriAd, S.SiparisTarihi;

Artık bu karmaşık sorgu yerine sadece SELECT * FROM MusteriSiparisOzeti; kullanılabilir.

Veri Soyutlama ve Tutarlılık

Görünümler, temel tablolardaki yapısal değişikliklerin (örneğin, bir sütunun adı değiştirildiğinde veya yeni bir tablo eklendiğinde) uygulamaları doğrudan etkilemesini engelleyebilir. Görünüm, temel tablolardaki değişikliklere rağmen aynı çıktıyı sağlayacak şekilde yeniden tanımlanabilir. Bu, uygulamaların temel veri yapısından soyutlanmasını sağlayarak veri bağımsızlığını artırır ve tutarlı bir arayüz sunar.

Yeniden Kullanılabilir Mantık

Belirli bir iş kuralını veya veri perspektifini içeren bir sorgu, bir görünüm olarak tanımlanarak veritabanının farklı bölümlerinde veya farklı uygulamalarda tekrar tekrar kullanılabilir. Bu, kod tekrarını önler ve bakım kolaylığı sağlar.

Görünüm Oluşturma ve Güncelleme

Bir görünüm, CREATE VIEW komutuyla oluşturulur. Mevcut bir görünümün tanımını değiştirmek için ALTER VIEW veya CREATE OR REPLACE VIEW (destekleyen veritabanlarında) kullanılır. Bir görünümü kaldırmak için ise DROP VIEW komutu kullanılır.


-- Görünüm Oluşturma
CREATE VIEW AktifMusteriler AS
SELECT MusteriID, Ad, Soyad, Email
FROM Musteriler
WHERE Durum = 'Aktif';

-- Görünüm Güncelleme (ALTER VIEW)
ALTER VIEW AktifMusteriler AS
SELECT MusteriID, Ad, Soyad, Email, Telefon
FROM Musteriler
WHERE Durum = 'Aktif';

-- Görünüm Güncelleme (CREATE OR REPLACE VIEW) - PostgreSQL, Oracle
CREATE OR REPLACE VIEW AktifMusteriler AS
SELECT MusteriID, Ad, Soyad, Email, Telefon
FROM Musteriler
WHERE Durum = 'Aktif' AND KayitTarihi > '2023-01-01';

-- Görünüm Silme
DROP VIEW AktifMusteriler;

Güncellenebilir Görünümler (Updatable Views)

Tüm görünümler güncellenebilir değildir. Bir görünüm aracılığıyla temel tablolardaki verileri INSERT, UPDATE veya DELETE işlemleriyle değiştirebilmek için belirli koşulların sağlanması gerekir:

  • Görünüm tek bir temel tabloya dayanmalıdır.
  • Görünüm, DISTINCT, GROUP BY, HAVING, kümeleme fonksiyonları (SUM, AVG, COUNT) veya UNION gibi yapılar içermemelidir.
  • Görünüm, hesaplanmış sütunlar içermemelidir.
  • Join içeren görünümler genellikle güncellenemez (bazı veritabanları belirli koşullar altında basit join’li görünümlere izin verebilir).

WITH CHECK OPTION ifadesi, bir görünüm aracılığıyla eklenen veya güncellenen satırların görünümün WHERE koşuluna uymasını sağlar. Eğer uymazsa, işlem reddedilir.


CREATE VIEW DüsükMaasliCalisanlar AS
SELECT CalisanID, Ad, Soyad, Maas
FROM Calisanlar
WHERE Maas < 50000
WITH CHECK OPTION;

-- Bu işlem başarılı olur
INSERT INTO DüsükMaasliCalisanlar (CalisanID, Ad, Soyad, Maas)
VALUES (101, 'Ayşe', 'Yılmaz', 45000);

-- Bu işlem WITH CHECK OPTION nedeniyle başarısız olur
-- INSERT INTO DüsükMaasliCalisanlar (CalisanID, Ad, Soyad, Maas)
-- VALUES (102, 'Mehmet', 'Demir', 60000);

Sanal Tablolar ve Görünümler İlişkisi

“Sanal Tablo” terimi genellikle Görünüm (View) ile eş anlamlı olarak kullanılır. Görünümler, veri depolamadan, temel tabloların veya diğer görünümlerin verilerini bir sorgu sonucu olarak sunan mantıksal yapılardır. Bu nedenle, veritabanı sisteminde fiziksel olarak diskte yer kaplayan ve veri içeren “gerçek” tabloların aksine “sanal” bir tablo görünümü sunarlar.

Görünümler, geçici tablolar (temporary tables) veya tablo değerli fonksiyonlar (table-valued functions) ile karıştırılmamalıdır. Geçici tablolar, oturuma özel olarak oluşturulan ve veri depolayan fiziksel tablolardır. Tablo değerli fonksiyonlar ise, parametre alabilen ve bir tablo döndüren programatik bir yapıdır; bir sorgunun sonucunu döndürme konusunda görünümlere benzerler ancak daha dinamik ve parametre odaklıdırlar.

Performans ve Sınırlamalar

Görünümler, kendileri veri depolamadıkları için performansı doğrudan artırmazlar; bir görünüm sorgulandığında, altında yatan temel sorgu çalıştırılır. Çok karmaşık görünümler veya iyi indekslenmemiş temel tablolar üzerinde yapılan sorgular, performans sorunlarına yol açabilir. Bu gibi durumlarda, bazı veritabanı sistemleri “İndekslenmiş Görünümler” (SQL Server) veya “Materialized Views” (Oracle, PostgreSQL) gibi seçenekler sunar. Bu yapılar, görünümün sonucunu fiziksel olarak depolayarak sorgu performansını artırabilir, ancak bu durumda veri tazeliği ve depolama alanı gibi ek yönetim gereksinimleri ortaya çıkar.

SQL’deki görünümler veya sanal tablolar, veritabanı tasarımını ve yönetimini önemli ölçüde geliştiren vazgeçilmez araçlardır. Güvenliği artırırken karmaşık veri erişimini basitleştirir, veri soyutlama sağlayarak veritabanı yapısındaki değişikliklerin etkisini azaltır ve tekrar kullanılabilir mantık sunar. Görünümleri etkili bir şekilde kullanmak için onların nasıl çalıştığını, ne zaman güncellenebilir olduklarını ve performans etkilerini iyi anlamak, daha sağlam ve yönetilebilir veritabanı çözümleri oluşturmak için kritik öneme sahiptir.


SQL’de Alt Sorgular (Subquery) ve İç İçe Sorgular

SQL veritabanlarında karmaşık veri manipülasyonları ve sorgulamaları için güçlü bir araç olan alt sorgular (subquery) ve iç içe sorgular, temel SQL komutlarının ötesinde esneklik sunar. Bu yapılar, tek bir sorgu içinde birden fazla sorguyu birleştirerek daha spesifik ve detaylı sonuçlar elde etmemizi sağlar. Veri analizi, raporlama ve dinamik filtreleme gibi birçok senaryoda vazgeçilmez bir role sahiptirler, SQL yetkinliğinizi derinleştirmenin anahtarıdır.

Alt Sorgu (Subquery) Nedir?

Alt sorgu, bir SQL sorgusunun başka bir SQL sorgusunun içine gömülmesiyle oluşan yapıdır. Ana sorgu tarafından kullanılan bir değer, satır seti veya tablo seti döndürür. Genellikle parantez içine alınır ve ana sorgudan önce yürütülür (korele alt sorgular hariç). Alt sorgular, belirli bir koşulu karşılayan verileri filtrelemek, başka bir tablodan türetilmiş değerlerle karşılaştırmak veya dinamik olarak oluşturulan veri setlerini kullanmak için kullanılır.

Alt Sorguların Kullanım Alanları ve Türleri

Alt sorgular, SELECT, FROM, WHERE, HAVING cümlelerinde ve hatta INSERT, UPDATE, DELETE ifadelerinde kullanılabilir. Temel olarak üç ana türe ayrılırlar:

1. Skaler Alt Sorgular (Scalar Subqueries)

Tek bir değer (tek bir satır, tek bir sütun) döndüren alt sorgulardır. Genellikle SELECT veya WHERE cümlelerinde kullanılırlar.

Örnek: Belirli bir ürün kategorisindeki en yüksek fiyatı bulmak ve bu fiyata sahip ürünleri listelemek.

SELECT UrunAdi, Fiyat
FROM Urunler
WHERE Fiyat = (SELECT MAX(Fiyat) FROM Urunler WHERE KategoriID = 1);

Bu örnekte, (SELECT MAX(Fiyat) FROM Urunler WHERE KategoriID = 1) kısmı skaler bir alt sorgudur ve KategoriID’si 1 olan ürünlerin en yüksek fiyatını döndürür.

2. Satır Alt Sorguları (Row Subqueries)

Tek bir satır ancak birden fazla sütun döndüren alt sorgulardır. Genellikle WHERE veya HAVING cümlelerinde, karşılaştırma operatörleriyle (=, >, <) birlikte kullanılırlar.

Örnek: En yüksek fiyata sahip ürünün hem fiyatını hem de kategori ID’sini bulmak.

SELECT UrunAdi, Fiyat, KategoriID
FROM Urunler
WHERE (Fiyat, KategoriID) = (SELECT MAX(Fiyat), KategoriID FROM Urunler GROUP BY KategoriID ORDER BY MAX(Fiyat) DESC LIMIT 1);

Bu örnekte, ana sorgu sadece tek bir (Fiyat, KategoriID) ikilisi ile eşleşen bir ürünü döndürecektir. LIMIT 1 kullanımı, alt sorgunun tek bir satır döndürmesini sağlar.

3. Tablo Alt Sorguları (Table Subqueries)

Birden fazla satır ve birden fazla sütun döndüren alt sorgulardır. Genellikle FROM cümlesinde, türetilmiş bir tablo olarak kullanılırlar. Bunlara “İç İçe Sorgular” da denir ve geçici bir tablo gibi davranırlar.

Örnek: Her kategorideki ortalama ürün fiyatını bulup, bu ortalamadan daha pahalı olan ürünleri listelemek.

SELECT U.UrunAdi, U.Fiyat, K.KategoriAdi, OrtFiyatlar.OrtalamaFiyat
FROM Urunler U
INNER JOIN Kategoriler K ON U.KategoriID = K.KategoriID
INNER JOIN (
    SELECT KategoriID, AVG(Fiyat) AS OrtalamaFiyat
    FROM Urunler
    GROUP BY KategoriID
) AS OrtFiyatlar ON U.KategoriID = OrtFiyatlar.KategoriID
WHERE U.Fiyat > OrtFiyatlar.OrtalamaFiyat;

Bu örnekte, (SELECT KategoriID, AVG(Fiyat) AS OrtalamaFiyat FROM Urunler GROUP BY KategoriID) kısmı tablo alt sorgusudur ve her kategori için ortalama fiyatı hesaplayan bir türetilmiş tablo oluşturur. Bu türetilmiş tablo daha sonra ana sorgu ile birleştirilir.

Alt Sorguların Diğer Kullanım Senaryoları

WHERE ve HAVING ile Birlikte Kullanım:

  • IN, NOT IN, EXISTS, NOT EXISTS operatörleriyle sıkça kullanılırlar.

IN Örneği: Belirli bir şehirdeki müşterilerin verdiği siparişleri listele.

SELECT SiparisID, MusteriID, SiparisTarihi
FROM Siparisler
WHERE MusteriID IN (SELECT MusteriID FROM Musteriler WHERE Sehir = 'Ankara');

EXISTS Örneği: Hiç sipariş vermemiş müşterileri bul (Korele alt sorgu örneği).

SELECT MusteriAdi, Eposta
FROM Musteriler M
WHERE NOT EXISTS (SELECT 1 FROM Siparisler S WHERE S.MusteriID = M.MusteriID);

Burada, EXISTS ana sorgunun her satırı için iç sorguyu çalıştırır. Eğer iç sorgu herhangi bir sonuç döndürürse (EXISTS true olur), ana sorgu o satırı filtrelemez.

Korele (Correlated) Alt Sorgular:

Bu tür alt sorgular, dış (ana) sorgunun her satırı için bir kez çalışır ve dış sorgudan bir değer alır. Yukarıdaki EXISTS örneği buna güzel bir örnektir. Korele alt sorgular genellikle NOT EXISTS veya EXISTS ile birlikte kullanılır ve performansı etkileyebilirler, bu yüzden dikkatli kullanılmalıdırlar.

Örnek: Kendi kategorisindeki ortalama fiyattan daha pahalı olan ürünleri listele.

SELECT UrunAdi, Fiyat, KategoriID
FROM Urunler U1
WHERE Fiyat > (SELECT AVG(Fiyat) FROM Urunler U2 WHERE U2.KategoriID = U1.KategoriID);

Burada, U1.KategoriID ana sorgudan gelen bir değerdir ve iç sorgunun her çalışmasında değişir.

INSERT, UPDATE, DELETE ile Kullanım:

Alt sorgular, veri manipülasyonu (DML) ifadelerinde de dinamik değerler veya koşullar sağlamak için kullanılabilir.

INSERT Örneği: Bir tablodan seçilen verileri başka bir tabloya ekle.

INSERT INTO ArsivUrunler (UrunAdi, Fiyat, EklenmeTarihi)
SELECT UrunAdi, Fiyat, GETDATE() -- veya CURRENT_TIMESTAMP
FROM Urunler
WHERE StokAdedi = 0;

UPDATE Örneği: Belirli bir kategorideki ürün fiyatlarını, o kategorinin ortalama fiyatının %10 üzerine çıkar.

UPDATE Urunler U1
SET Fiyat = (SELECT AVG(Fiyat) * 1.10 FROM Urunler U2 WHERE U2.KategoriID = U1.KategoriID)
WHERE KategoriID IN (SELECT KategoriID FROM Kategoriler WHERE KategoriAdi = 'Elektronik');

Alt Sorguların Avantajları ve Dezavantajları

Avantajlar:

  • Kod Okunabilirliği: Karmaşık birleştirme (JOIN) işlemleri yerine, daha mantıksal ve okunabilir bir yol sunabilirler.
  • Modülerlik: Sorguyu daha küçük, yönetilebilir parçalara ayırır.
  • Esneklik: Neredeyse her SQL cümlesinde kullanılabilir ve çok çeşitli senaryolara uyum sağlar.
  • Dinamik Filtreleme: Statik değerler yerine dinamik olarak hesaplanan değerlerle filtreleme imkanı sunar.

Dezavantajlar:

  • Performans: Özellikle korele alt sorgular veya çok fazla iç içe geçmiş alt sorgular, performansı olumsuz etkileyebilir. Her dış satır için yeniden çalıştırılma maliyeti olabilir.
  • Anlaşılabilirlik (Yanlış Kullanımda): Aşırı iç içe geçmiş veya kötü yazılmış alt sorgular, okunabilirliği ve hata ayıklamayı zorlaştırabilir.
  • Alternatifler: Bazı durumlarda JOIN işlemleri, CTE’ler (Common Table Expressions) veya türetilmiş tablolar, alt sorgulara göre daha performanslı veya okunabilir çözümler sunabilir.

Ne Zaman ve Nasıl Kullanmalı?

Alt sorgular, bir değeri veya bir dizi değeri başka bir sorgunun sonucu olarak dinamik olarak belirlemeniz gerektiğinde idealdir. Genellikle, bir tablodaki veriyi aynı tablodaki veya başka bir tablodaki diğer verilerle karşılaştırmanız gerektiğinde tercih edilir. Performans endişesi taşıdığınızda, özellikle büyük veri setlerinde, alt sorguyu birleştirme (JOIN) işlemine dönüştürmenin mümkün olup olmadığını veya CTE kullanıp kullanamayacağınızı düşünmek faydalı olacaktır.

Sonuç

SQL’deki alt sorgular ve iç içe sorgular, veri manipülasyonunda ve analizinde esneklik ile güç sağlayan temel yapılardır. Skaler, satır ve tablo alt sorguları gibi çeşitli türleriyle, karmaşık iş mantığını tek bir sorgu içinde ifade etmenizi mümkün kılarlar. Doğru anlaşıldığında ve uygun şekilde kullanıldığında, SQL sorgularınızın okunabilirliğini artırırken, dinamik ve güçlü filtreleme yetenekleri sunarlar. Performans üzerindeki potansiyel etkileri nedeniyle dikkatli optimizasyon ve alternatiflerle değerlendirilmelidirler.


SQL’de Toplu İşlemler ve Gruplama (GROUP BY, HAVING)

SQL, büyük veri setlerini yönetme ve analiz etmede vazgeçilmez bir araçtır. Bu makalede, veri setlerini özetlemek ve anlamlı bilgiler çıkarmak için kullanılan toplu işlemleri, yani COUNT, SUM, AVG, MIN, MAX gibi fonksiyonları ele alacağız. Ardından, verileri belirli kriterlere göre gruplandıran GROUP BY yan tümcesinin ve bu gruplanmış veriler üzerinde koşul uygulayan HAVING yan tümcesinin derinlemesine incelenmesi yapılacaktır. Bu yaklaşımlar, karmaşık raporlama ve analiz ihtiyaçlarını karşılamak için kritik öneme sahiptir.

SQL’de Toplu İşlemler (Aggregate Functions)

Toplu işlemler, bir grup üzerinde hesaplama yapan ve tek bir sonuç değeri döndüren SQL fonksiyonlarıdır. Bu fonksiyonlar genellikle veri setlerinin özetlenmesi, raporlanması ve analiz edilmesi süreçlerinde kullanılır. İşte en yaygın kullanılan toplu fonksiyonlar:

  • COUNT(): Belirtilen ölçüte uyan satır sayısını döndürür. Örneğin, bir tablodaki toplam kayıt sayısını veya belirli bir koşulu sağlayan kayıt sayısını saymak için kullanılır.
  • SUM(): Bir sayısal sütundaki tüm değerlerin toplamını hesaplar. Örneğin, toplam satış miktarını veya toplam geliri bulmak için idealdir.
  • AVG(): Bir sayısal sütundaki tüm değerlerin ortalamasını hesaplar. Ortalama puanları, ortalama sipariş değerlerini veya ortalama yaşları bulmak için kullanılabilir.
  • MIN(): Bir sütundaki en küçük değeri döndürür. En düşük fiyatı, en erken tarihi veya en küçük sayısal değeri bulmak için faydalıdır.
  • MAX(): Bir sütundaki en büyük değeri döndürür. En yüksek fiyatı, en geç tarihi veya en büyük sayısal değeri bulmak için kullanılır.

Bu fonksiyonlar genellikle SELECT ifadesinde tek başına veya GROUP BY yan tümcesi ile birlikte kullanılır.

SELECT COUNT(*) AS ToplamMusteriSayisi FROM Musteriler;
SELECT SUM(SatisTutari) AS ToplamSatis FROM Siparisler;
SELECT AVG(Fiyat) AS OrtalamaFiyat FROM Urunler;

GROUP BY Yan Tümcesi

GROUP BY yan tümcesi, SQL sorgularında benzer değerlere sahip satırları gruplandırmak için kullanılır. Bu gruplama işlemi, toplu fonksiyonların her bir grup için ayrı ayrı uygulanmasını sağlar. Yani, veri setinizdeki her benzersiz kategori veya değer için ayrı bir özet satırı elde edersiniz.

GROUP BY Ne İşe Yarar?

Diyelim ki bir satış tablonuz var ve her bir müşteri için toplam satış tutarını görmek istiyorsunuz. GROUP BY olmadan bu işlemi yapmak zor veya imkansızdır. GROUP BY ile müşteri ID’sine göre gruplandırma yaparak, her bir müşterinin tüm siparişlerini tek bir grupta toplayabilir ve ardından bu grup üzerinde SUM() fonksiyonunu uygulayarak toplam satış tutarını bulabilirsiniz.

GROUP BY Sözdizimi ve Kullanımı

GROUP BY yan tümcesi, FROM ve WHERE yan tümcelerinden sonra, ORDER BY yan tümcesinden önce gelir. SELECT listesinde yer alan, toplu fonksiyonda kullanılmayan tüm sütunların GROUP BY yan tümcesinde belirtilmesi gerekir.

SELECT sutun1, COUNT(sutun2)
FROM tablo_adi
WHERE kosul
GROUP BY sutun1
ORDER BY sutun1;

Örnek: Müşterilere Göre Toplam Sipariş Sayısı ve Tutarı

Bir Siparisler tablonuz olduğunu ve bu tabloda MusteriID, SiparisID ve SiparisTutari sütunlarının bulunduğunu varsayalım:

SELECT
    MusteriID,
    COUNT(SiparisID) AS ToplamSiparisSayisi,
    SUM(SiparisTutari) AS ToplamSiparisTutari
FROM
    Siparisler
GROUP BY
    MusteriID
ORDER BY
    ToplamSiparisTutari DESC;

Bu sorgu, her bir müşteri için kaç sipariş verdiklerini ve bu siparişlerin toplam tutarını hesaplar. Sonuçlar, toplam sipariş tutarına göre azalan şekilde sıralanır.

HAVING Yan Tümcesi

HAVING yan tümcesi, GROUP BY tarafından oluşturulan grupları filtrelemek için kullanılır. WHERE yan tümcesi bireysel satırları gruplamadan önce filtrelerken, HAVING yan tümcesi toplu fonksiyon sonuçlarına dayalı olarak grupları filtreler. Bu ayrım kritik öneme sahiptir.

WHERE ve HAVING Arasındaki Fark

  • WHERE: Tabloda bulunan ham veriye uygulanır, yani gruplama yapılmadan önce tek tek satırlar üzerinde koşul kontrolü yapar. Toplu fonksiyonlar WHERE içinde doğrudan kullanılamaz.
  • HAVING: GROUP BY işleminden sonra, toplu fonksiyonların sonuçlarına uygulanır. Gruplanmış veriler üzerinde koşul kontrolü yapar.

HAVING Sözdizimi ve Kullanımı

HAVING yan tümcesi, GROUP BY yan tümcesinden sonra gelir.

SELECT sutun1, COUNT(sutun2)
FROM tablo_adi
WHERE kosul
GROUP BY sutun1
HAVING toplu_fonksiyon_kosulu
ORDER BY sutun1;

Örnek: Toplam Sipariş Tutarı 1000 TL’yi Aşan Müşteriler

Yukarıdaki Siparisler tablosu örneğini kullanarak, sadece toplam sipariş tutarı 1000 TL’yi aşan müşterileri listelemek isteyelim:

SELECT
    MusteriID,
    COUNT(SiparisID) AS ToplamSiparisSayisi,
    SUM(SiparisTutari) AS ToplamSiparisTutari
FROM
    Siparisler
GROUP BY
    MusteriID
HAVING
    SUM(SiparisTutari) > 1000
ORDER BY
    ToplamSiparisTutari DESC;

Bu sorgu, önce her müşteri için siparişleri gruplar, sonra her grubun toplam sipariş tutarını hesaplar ve son olarak sadece toplam tutarı 1000’den büyük olan grupları (müşterileri) listeler.

GROUP BY ve HAVING ile İleri Seviye Kullanım Senaryoları

Bu iki yan tümce, karmaşık veri analizi ve raporlama ihtiyaçlarını karşılamak için birleştirildiğinde gerçek gücünü gösterir. Örneğin, belirli bir ürün kategorisinde ortalama satış tutarı belirli bir değerin üzerinde olan bölgeleri veya son 6 ayda belirli sayıda üründen fazla satan personeli bulmak gibi senaryolar bu yapılarla kolayca çözülebilir.

-- Örnek: Belirli bir tarihten sonra 5'ten fazla sipariş veren müşteriler
SELECT
    MusteriID,
    COUNT(SiparisID) AS ToplamSiparisSayisi
FROM
    Siparisler
WHERE
    SiparisTarihi > '2023-01-01'
GROUP BY
    MusteriID
HAVING
    COUNT(SiparisID) > 5;

Burada WHERE, ilk olarak belirli bir tarihten sonraki siparişleri filtreler. Ardından GROUP BY, kalan siparişleri müşteri bazında gruplar. Son olarak HAVING, bu gruplardan sadece 5’ten fazla siparişi olanları seçer.

Performans İpuçları

Büyük veri setleriyle çalışırken GROUP BY ve HAVING kullanırken performansı göz önünde bulundurmak önemlidir:

  • İlgili sütunlar üzerinde indeksler oluşturmak, sorgu performansını önemli ölçüde artırabilir. Özellikle GROUP BY içinde kullanılan sütunlar indekslenmelidir.
  • WHERE yan tümcesini mümkün olduğunca erken ve etkili bir şekilde kullanarak veri setini küçültmek, GROUP BY ve HAVING işlemlerinin daha az veri üzerinde çalışmasını sağlar.
  • Gereksiz sütunları SELECT listesine dahil etmeyin. Sadece ihtiyacınız olan verileri çekmek, sorgu optimizasyonuna yardımcı olur.

GROUP BY ve HAVING yan tümceleri, SQL’de veri analizi ve raporlama için güçlü araçlardır. Bu makalede ele alınan toplu fonksiyonlarla birleştiğinde, büyük veri setlerinden derinlemesine içgörüler elde etmek mümkün hale gelir. Verileri anlamlı gruplara ayırmak ve bu gruplar üzerinde koşullar uygulamak, işletmelerin bilinçli kararlar almasına yardımcı olur. Doğru kullanıldığında, bu SQL yapıları veri tabanlarından maksimum verimlilikle bilgi çekme yeteneğinizi önemli ölçüde artırır. Bu yetkinlik, modern veri odaklı dünyada hayati öneme sahiptir.


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