VTYS Kod Arşivi

Her konu için 10+ Farklı Senaryo. Toplam 50x Daha Fazla İçerik.

01

Hafta 1: Varlık & İlişki Tipleri (ER Diagram)

Veritabanı tasarımının temeli. Hangi tablo diğerine nasıl bağlanır?

1.1 İlişki Senaryoları Kütüphanesi

1'e 1 (One-to-One) Örnekleri
  • Vatandaş - TC Kimlik: Bir kişinin bir TC'si olur.
  • Müdür - Okul: Bir okulun bir müdürü olur.
  • CEO - Şirket: Bir şirketin tek CEO'su olur.
  • Hasta - Dosya: Hastanın o anki aktif dosyası tektir.
1'e Çok (One-to-Many) Örnekleri
  • Anne - Çocuk: Bir annenin çok çocuğu olabilir.
  • Müşteri - Sipariş: Müşteri çok sipariş verebilir.
  • Yazar - Kitap: Bir yazar çok kitap yazabilir.
  • Kategori - Ürün: Bir kategoride çok ürün olur.
Çok'a Çok (Many-to-Many) Örnekleri

Arada mutlaka bağlantı tablosu gerekir!

  • Öğrenci - Ders: Öğrenci çok ders alır, dersi çok öğrenci alır. (Tablo: OgrenciDers)
  • Doktor - Hasta: Bir doktor çok hastaya bakar, hasta çok doktora gider. (Tablo: Randevular)
  • Oyuncu - Film: (Tablo: FilmEkibi)
Zayıf Varlık (Weak Entity)

Kendi başına var olamayan varlık.

  • Oda: Otel olmadan oda olamaz. (PK: OtelID + OdaNo)
  • Fatura Kalemi: Fatura olmadan satır olamaz.

1.2 Tanımlar

Varsayılan database

MasterDB
Veritabanının Kalbi

Sistem üzerinde kullanıcıların açmış olduğu database’lerin listesini, yine kullanıcı tarafından veya sistem tarafından belirlenen login bilgileri gibi bir çok veriyi saklar ve yönetir.

MsDB
Görevleri ve zamanlanmış işleri tutar

Job : Bizim belirttiğimiz zaman aralıklarında çalışan ve kendisine atanmış görevleri yerine getiren bir mekanizmadır.

TempDB
Geçici Veritabanı

Geçici olarak sistem veya kullanıcı objelerini saklayabilir. Bu objeler , tablo, stored procedur vs. olabilir.

ModelDB
Varsayılan Veritabanı Şablonu

Yeni açılan tüm veri tabanlarına modellik eder. Yani SQL server üzerinden açmış olduğumuz tüm veri tabanları model veri tabanının birebir kopyasıdır.

1.3 GLOBAL SENARYO: E-Ticaret Veritabanı Mimarisi

Gerçek hayatta bu ilişkiler nasıl kurulur? İşte "Hepsiburada/Trendyol" benzeri bir yapının mimarisi:

-- 1. Müşteriler (Ana Tablo) [Musteriler] TABLOSU L-> ID (PK) L-> AdSoyad L-> Email -- 2. Ürünler (Ana Tablo - Kategoriler ile N:1 İlişkili) [Urunler] TABLOSU L-> ID (PK) L-> Ad L-> Fiyat L-> KategoriID (FK) -> Kategoriler(ID) -- 3. Siparişler (İşlem Tablosu - Müşteri ile N:1 İlişkili) [Siparisler] TABLOSU L-> ID (PK) L-> SiparisNo L-> Tarih L-> MusteriID (FK) -> Musteriler(ID) -- 4. SiparisDetay (Ara Tablo - Sipariş ve Ürün ile N:N İlişkiyi Çözer) [SiparisDetay] TABLOSU L-> SiparisID (FK) -> Siparisler(ID) L-> UrunID (FK) -> Urunler(ID) L-> Adet L-> BirimFiyat L-> PRIMARY KEY (SiparisID, UrunID) -- Composite Key -- ANALİZ: -- Bir Müşteri Çok Sipariş verebilir (1:N). -- Bir Siparişte Çok Ürün olabilir, Bir Ürün Çok Siparişte olabilir (N:N -> Ara Tablo: SiparisDetay).
02

Hafta 2: Tablo Oluşturma (DDL)

CREATE TABLE ve ALTER TABLE için tüm olasılıklar.

2.1 Constraint (Kısıtlama) Arşivi

Verinizi koruyan zırhlar. Her biri için ayrı örnek:

1. PRIMARY KEY (Kimlik)
ID INT PRIMARY KEY IDENTITY(1,1)

Tablonun kalbi. Tekrarlanamaz, NULL olamaz.

2. FOREIGN KEY (Bağlantı)
KategoriID INT REFERENCES Kategoriler(ID)

Başka tabloya referans verir. Olmayan kategori girilemez.

3. UNIQUE (Tekillik)
Email VARCHAR(100) UNIQUE

Aynı mail ile ikinci üye olamaz.

4. CHECK (Kontrol)
Fiyat DECIMAL CHECK (Fiyat > 0)

Fiyat eksi girilemez. Mantık kontrolü.

5. DEFAULT (Varsayılan)
Durum BIT DEFAULT 1

Veri girilmezse otomatik 'Aktif' olsun.

6. NOT NULL (Zorunluluk)
Ad Soyad NVARCHAR(50) NOT NULL

Bu alan boş bırakılamaz.

2.2 Tam Donanımlı Tablo Örneği

CREATE TABLE Personeller ( PersonelID INT PRIMARY KEY IDENTITY(1,1), TCNo CHAR(11) UNIQUE NOT NULL, -- Herkesin TC'si farklı olmalı Ad NVARCHAR(50) NOT NULL, Soyad NVARCHAR(50) NOT NULL, Maas DECIMAL(18,2) CHECK (Maas >= 17002), -- Asgari ücret altı olamaz IseGiris DATETIME DEFAULT GETDATE(), -- Tarih girilmezse bugün olsun DepartmanID INT, -- Foreign Key Tanımı (Best Practice: En altta tanımlamak) CONSTRAINT FK_Personel_Departman FOREIGN KEY (DepartmanID) REFERENCES Bolumler(BolumID) ON DELETE SET NULL -- Bölüm silinirse personel silinmesin, boşa düşsün );

2.3 İleri Düzey Senaryolar (Hazine Sandığı)

Composite Primary Key (Çift Anahtar)
CREATE TABLE OgrenciDers ( OgrenciID INT, DersID INT, Yil INT, PRIMARY KEY (OgrenciID, DersID, Yil) );
Mantık: Bir öğrenci aynı dersi aynı yıl içinde sadece bir kez alabilir. Üç kolonun bileşimi benzersiz olmalıdır.
Tarih Kontrolü (Check Constraint)
CREATE TABLE Etkinlikler ( Baslangic DATETIME, Bitis DATETIME, CHECK (Bitis > Baslangic) );
Mantık: Bitiş tarihi asla başlangıçtan önce olamaz. Veritabanı seviyesinde hata yakalar.
Dinamik Varsayılan (Default)
CREATE TABLE Loglar ( IslemID UNIQUEIDENTIFIER DEFAULT NEWID(), Tarih DATETIME DEFAULT GETDATE(), Kullanici VARCHAR(50) DEFAULT SUSER_SNAME() );
Otomasyon: Veri girilmezse otomatik UUID üretir, şu anki zamanı basar ve işlemi yapan kullanıcıyı kaydeder.

2.4 GLOBAL SENARYO: "Büyük E-Ticaret" Tablo Kurulumu

Tek tek kolon oluşturmak kolaydır. Ama ilişkili tabloları doğru sırayla ve constraint'lerle kurmak mühendislik ister.

FULL SCRIPT (Copy-Paste Ready)
-- 1. Önce Bağımsız Tablolar (Hiçbir yere FK ile bağlı olmayanlar) CREATE TABLE Kategoriler ( ID INT PRIMARY KEY IDENTITY(1,1), Ad NVARCHAR(50) NOT NULL UNIQUE, -- Aynı kategori ismi olamaz AktifMi BIT DEFAULT 1 ); -- 2. Ana Tablolar (Kategorilere bağlananlar) CREATE TABLE Urunler ( ID INT PRIMARY KEY IDENTITY(1,1), KategoriID INT, Kod CHAR(10) UNIQUE, -- Barkod benzeri tekil kod Ad NVARCHAR(100) NOT NULL, Fiyat DECIMAL(18,2) CHECK (Fiyat >= 0), -- Negatif fiyat koruması Stok INT DEFAULT 0, CONSTRAINT FK_Urun_Kategori FOREIGN KEY (KategoriID) REFERENCES Kategoriler(ID) ); -- 3. İşlem Tabloları (Müşteri ve Sipariş) CREATE TABLE Musteriler ( ID INT PRIMARY KEY IDENTITY(1,1), Email VARCHAR(100) NOT NULL UNIQUE, -- Login için unique email şart SifreHash CHAR(64) NOT NULL, KayitTarihi DATETIME DEFAULT GETDATE() ); CREATE TABLE Siparisler ( ID INT PRIMARY KEY IDENTITY(1,1), MusteriID INT NOT NULL, Tarih DATETIME DEFAULT GETDATE(), Durum TINYINT CHECK (Durum IN (0,1,2,3)), -- 0:Bekliyor, 1:Onay, 2:Kargo, 3:İptal CONSTRAINT FK_Siparis_Musteri FOREIGN KEY (MusteriID) REFERENCES Musteriler(ID) ); -- 4. Ara Tablo (Many-to-Many Çözümü) CREATE TABLE SiparisDetay ( SiparisID INT, UrunID INT, Adet INT CHECK (Adet > 0), BirimFiyat DECIMAL(18,2), -- O günkü fiyatı saklarız! (Ürün fiyatı değişebilir) PRIMARY KEY (SiparisID, UrunID), -- Composite PK (Bir siparişte aynı ürün 1 kez geçer, adeti artar) FOREIGN KEY (SiparisID) REFERENCES Siparisler(ID), FOREIGN KEY (UrunID) REFERENCES Urunler(ID) );
03

Hafta 3: Veri İşleme (DML)

Insert, Update, Delete, Select. Binbir türlü hali.

3.1 INSERT (Ekleme) Varyasyonları

Varyasyon 1: Tüm Kolonlara Ekleme
INSERT INTO Urunler VALUES ('Laptop', 15000, 10); -- Tehlikeli: Tablo yapısı değişirse patlar.
Varyasyon 2: Seçili Kolonlara Ekleme (Doğrusu)
INSERT INTO Urunler (Ad, Fiyat) VALUES ('Mouse', 250); -- Stok girmedik, DEFAULT değeri (0) atanır. ID otomatik artar.
Varyasyon 3: Toplu Ekleme (Bulk Insert)
INSERT INTO Kategoriler (Ad) VALUES ('Elektronik'), ('Giyim'), ('Gıda');
Varyasyon 4: Başka Tablodan Kopyalama
INSERT INTO ArsivUrunler (Ad, Fiyat) SELECT Ad, Fiyat FROM Urunler WHERE Stok = 0; -- Biten ürünleri arşive taşıma mantığı.

3.2 SELECT (Sorgulama) Arsenaliniz

Temel Filtreler
WHERE Fiyat > 100 AND Stok < 20
Aralık (Range)
WHERE Tarih BETWEEN '2023-01-01' AND '2023-12-31'
Benzerlik (Like)
WHERE Ad LIKE 'A%' (A ile başlayanlar)
Liste (In)
WHERE Sehir IN ('İstanbul', 'Ankara')
Sıralama (Order By)
ORDER BY Fiyat DESC, Ad ASC
Limit (Top)
SELECT TOP 5 * FROM Urunler

3.3 DML Ustalık Sınıfı

CASE WHEN ile Akıllı SELECT
SELECT Ad, Maas, CASE WHEN Maas < 17002 THEN 'Düşük' WHEN Maas BETWEEN 17002 AND 30000 THEN 'Orta' ELSE 'Yüksek' END AS GelirGrubu FROM Personel;
Analiz: Veriyi çekerken anlık olarak kategorize eder. Raporlamada hayati önem taşır.
DELETE with JOIN (İlişkisel Silme)
DELETE S FROM Satislar S INNER JOIN Urunler U ON S.UrunID = U.ID WHERE U.KategoriID = 5;
Senaryo: Kategori ID'si 5 olan ürünlerin TÜM satış geçmişini temizler.
UPDATE with FROM (Toplu Zam)
UPDATE U SET U.Fiyat = U.Fiyat * 1.20 FROM Urunler U INNER JOIN Markalar M ON U.MarkaID = M.ID WHERE M.Ad = 'PremiumBrand';
Senaryo: Sadece 'PremiumBrand' markalı ürünlere %20 zam yapar.

3.4 GLOBAL SENARYO: Günlük Operasyonlar (Full Cycle)

Bir e-ticaret sitesinin bir gün içinde çalıştırdığı SQL komutları zinciri:

1. SİPARİŞ OLUŞTURMA (INSERT)
-- Yeni sipariş başlığı aç INSERT INTO Siparisler (MusteriID, Durum) VALUES (101, 0); -- Sipariş detaylarını ekle (Sepetteki ürünler) INSERT INTO SiparisDetay (SiparisID, UrunID, Adet, BirimFiyat) VALUES (500, 1, 2, 25000), -- 2 adet Laptop (500, 5, 1, 500); -- 1 adet Mouse
2. STOK GÜNCELLEME (UPDATE)
-- Satılan ürünleri stoktan düş UPDATE Urunler SET Stok = Stok - 2 WHERE ID = 1;
3. HATALI SİPARİŞİ İPTAL ETME (SOFT DELETE)
-- Fiziksel silmek yerine durumunu 'İptal' (3) yapıyoruz. UPDATE Siparisler SET Durum = 3, IptalNedeni = 'Stok Hatası' WHERE ID = 500; -- (Opsiyonel) Eğer veritabanı şişmesin diye gerçekten sileceksek: -- DELETE FROM SiparisDetay WHERE SiparisID = 500; -- Önce detayları sil (Child) -- DELETE FROM Siparisler WHERE ID = 500; -- Sonra başlığı sil (Parent)
04

Hafta 4: Tablo Birleştirme (JOIN)

İlişkisel veritabanının gücü. Her JOIN türü için 4'er farklı senaryo.

4.1 INNER JOIN (Kesişim) Senaryoları

Sadece her iki tabloda da eşleşen verileri getirir. "Eksik veri istemiyorum" modudur.

Senaryo A: Öğrenci ve Bölümü
SELECT O.Ad, B.BolumAdi FROM Ogrenciler O INNER JOIN Bolumler B ON O.BolumID = B.ID; -- Bölümü olmayan öğrenci gelmez. Öğrencisi olmayan bölüm gelmez.
Senaryo B: Satılan Ürünler
SELECT S.Tarih, U.UrunAdi FROM Satislar S INNER JOIN Urunler U ON S.UrunID = U.ID; -- Sadece satışı yapılmış ürünleri listeler.
Senaryo C: Üçlü Birleştirme (3 Table Join)
SELECT M.Ad, S.Tarih, U.UrunAdi FROM Musteriler M INNER JOIN Satislar S ON M.ID = S.MusteriID INNER JOIN Urunler U ON S.UrunID = U.ID; -- Kim, Ne zaman, Ne aldı?

4.2 LEFT JOIN (Koruyucu) Senaryoları

Sol tablodaki (FROM) her şeyi getir, sağda karşılığı yoksa NULL bas. "Veri kaybetme" modudur.

Senaryo A: Hiç Sipariş Vermeyen Müşterileri Bul
SELECT M.Ad, S.SiparisNo FROM Musteriler M LEFT JOIN Siparisler S ON M.ID = S.MusteriID WHERE S.SiparisNo IS NULL; -- Kritik Rapor: Aktif olmayan müşterileri bulur.
Senaryo B: Tüm Personel ve Projeleri
SELECT P.Ad, Pr.ProjeAdi FROM Personel P LEFT JOIN Projeler Pr ON P.ProjeID = Pr.ID; -- Projesi olmayan personel de listede görünür (ProjeAdi NULL yazar).

4.3 Sık Kullanılan JOIN Kalıpları

Gerçek hayatta en çok karşınıza çıkacak senaryolar.

Satışı Olmayan Ürünleri Bul (LEFT JOIN)
SELECT U.Ad, U.Stok FROM Urunler U LEFT JOIN Satislar S ON U.ID = S.UrunID WHERE S.ID IS NULL;
Mantık: Ürünleri sola koy, satışları sağa. Satışı olmayanların sağ tarafı NULL kalır.
Hem Kategori Hem Marka Bilgisi (Multi Join)
SELECT U.Ad, K.Ad AS Kategori, M.Ad AS Marka FROM Urunler U INNER JOIN Kategoriler K ON U.KategoriID = K.ID INNER JOIN Markalar M ON U.MarkaID = M.ID;
Zincirleme: Ürün tablosunu hem kategoriye hem markaya bağlayarak detaylı rapor çekeriz.

4.4 JOIN Ustalık Sınıfı (Nadir ama Kritik)

SELF JOIN (Kendine Referans)
SELECT Calisan.Ad AS Personel, Yonetici.Ad AS Mudur FROM Personel Calisan LEFT JOIN Personel Yonetici ON Calisan.MudurID = Yonetici.ID;
Hiyerarşi: Aynı tablodaki ast-üst ilişkisini kurar. CEO'nun müdürü olmadığı için LEFT JOIN şarttır.
CROSS JOIN (Kartezyen Çarpımı)
SELECT R.Renk, B.Beden FROM Renkler R CROSS JOIN Bedenler B;
Kombinasyon: 5 renk ve 4 beden varsa 20 satırlık tüm olasılıkları üretir. WHERE koşulu OLMAZ.
FULL OUTER JOIN (Tam Birleşme)
SELECT O.Ad, K.KursAdi FROM Ogrenciler O FULL OUTER JOIN KursKayit K ON O.ID = K.OgrenciID;
Her Şey Gelsin: Hem kursa gitmeyen öğrencileri hem de öğrencisi olmayan kursları aynı anda listeler.

4.3 GLOBAL SENARYO: "THE BOSS REPORT" (Yönetici Raporu)

Patron geldi ve şunu istedi: "Hangi müşteri, hangi kategoriden, ne zaman, kaç paralık alışveriş yapmış? Bana tek listede ver!" İşte JOIN sanatı burada başlar.

5 TABLOLU DEV JOIN SORGUSU
SELECT M.AdSoyad AS Musteri, S.SiparisNo, S.Tarih, U.Ad AS UrunAdi, K.Ad AS Kategori, SD.Adet, SD.BirimFiyat, (SD.Adet * SD.BirimFiyat) AS ToplamTutar -- Hesaplamalı Kolon FROM Siparisler S -- 1. Müşteriyi bağla (Kimin siparişi?) INNER JOIN Musteriler M ON S.MusteriID = M.ID -- 2. Detayları bağla (İçinde ne var?) INNER JOIN SiparisDetay SD ON S.ID = SD.SiparisID -- 3. Ürün isimlerini getir (Hangi ürün?) INNER JOIN Urunler U ON SD.UrunID = U.ID -- 4. Kategori ismini getir (Hangi reyon?) INNER JOIN Kategoriler K ON U.KategoriID = K.ID WHERE S.Tarih > '2023-01-01' -- Sadece bu yıl AND S.Durum = 1 -- Sadece onaylananlar ORDER BY S.Tarih DESC; -- En yeni sipariş en üstte
05

Hafta 5: Alt Sorgular (Subqueries)

Sorgu içinde sorgu yazmanın her yolu. Tekil, Liste, Tablo.

5.1 Temel Alt Sorgular (Scalar & List)

Örnek 1: Ortalama Üstü Fiyatlar
SELECT * FROM Urunler WHERE Fiyat > (SELECT AVG(Fiyat) FROM Urunler);

Tek bir değer döner (Scalar Subquery).

Örnek 2: En Pahalı Ürünün Detayı
SELECT Ad, Fiyat FROM Urunler WHERE Fiyat = (SELECT MAX(Fiyat) FROM Urunler);
Örnek 3: İstanbul'daki Çalışanlar
SELECT * FROM Personel WHERE BolumID IN ( SELECT ID FROM Bolumler WHERE Sehir='İstanbul' );
Senaryo A: Correlated Subquery (İlişkili Alt Sorgu)
SELECT P.Ad, P.Maas FROM Personel P WHERE P.Maas > ( SELECT AVG(Maas) FROM Personel WHERE DepartmanID = P.DepartmanID );
Analiz: "Kendi departman ortalamasından yüksek maaş alanları" bulur. Dış sorgudaki her satır için iç sorgu tekrar çalışır (Performans maliyeti yüksektir ama çok güçlüdür).

5.2 EXISTS vs IN (Performans Savaşı)

Mülakatların vazgeçilmez sorusu. Ne zaman hangisi kullanılır?

EXISTS (Varlık Kontrolü)
SELECT * FROM Musteriler M WHERE EXISTS ( SELECT 1 FROM Siparisler S WHERE S.MusteriID = M.ID );

True/False döner. İlk eşleşmeyi bulunca durur. Büyük tablolarda çok hızlıdır.

NOT EXISTS (Olmayanları Bul)
SELECT * FROM Urunler U WHERE NOT EXISTS ( SELECT 1 FROM Satislar S WHERE S.UrunID = U.ID );

Hiç satılmamış ürünleri bulmanın en performanslı/doğru yolu (NULL safe).

Örnek 4: Satış Yapmayan Personel
SELECT * FROM Personel WHERE ID NOT IN ( SELECT DISTINCT PersonelID FROM Satislar WHERE PersonelID IS NOT NULL );

Ekstra Alt Sorgu Örnekleri

Maaşı Bölüm Ortalamasından Yüksek Olanlar
SELECT Ad, Maas, BolumID FROM Personel P WHERE Maas > ( SELECT AVG(Maas) FROM Personel WHERE BolumID = P.BolumID );
Correlated Subquery: Her satır için iç sorgu tekrar çalışır. Bölüm bazlı kıyaslama yapar.
En Çok Satılan Ürünü Bul
SELECT Ad FROM Urunler WHERE ID = ( SELECT TOP 1 UrunID FROM Satislar GROUP BY UrunID ORDER BY COUNT(*) DESC );
İç İçe Mantık: Önce en çok satanı bul, sonra onun adını getir.

5.2 EXISTS ve ANY/ALL Kullanımı

Daha performanslı ve esnek kontroller.

Senaryo A: EXISTS ile Varlık Kontrolü
SELECT Ad FROM Musteriler M WHERE EXISTS ( SELECT 1 FROM Siparisler S WHERE S.MusteriID = M.ID AND S.Tutar > 10000 ); -- 10.000 TL üzeri siparişi olan müşterileri getir. IN yerine EXISTS daha performanslıdır.
Senaryo B: ANY (Herhangi Biri)
SELECT Ad, Maas FROM Personel WHERE Maas > ANY ( SELECT Maas FROM Personel WHERE Unvan = 'Stajyer' ); -- En düşük stajyer maaşından bile yüksek alanları getirir.
Senaryo C: ALL (Hepsi)
SELECT Ad, Maas FROM Personel WHERE Maas > ALL ( SELECT Maas FROM Personel WHERE Unvan = 'Stajyer' ); -- En yüksek stajyer maaşından bile yüksek alanları (TÜM stajyerleri geçenleri) getirir.

5.3 Subquery Ustalık Sınıfı

Derived Table (FROM İçinde Sorgu)
SELECT Ortalama.Sehir, Ortalama.OrtMaas FROM ( SELECT Sehir, AVG(Maas) as OrtMaas FROM Personel GROUP BY Sehir ) AS Ortalama WHERE Ortalama.OrtMaas > 25000;
Sanal Tablo: Önce bellekte geçici bir tablo (Derived Table) oluşturup, onun üzerinden filtreleme yaparız. Alias (AS) zorunludur.
Subquery ile UPDATE (Bilinmeyen Değer)
UPDATE Urunler SET Fiyat = Fiyat * 1.10 WHERE KategoriID = ( SELECT ID FROM Kategoriler WHERE Ad = 'Elektronik' );
Dinamik Güncelleme: Elektronik kategorisinin ID'sini bilmesek bile isminden bulup ürünlerine zam yaparız.
Subquery ile INSERT (Veri Transferi)
INSERT INTO VIPMusteriler (Ad, Ciro) SELECT Ad, (SELECT SUM(Tutar) FROM Siparisler WHERE MusteriID = M.ID) FROM Musteriler M WHERE (SELECT SUM(Tutar) FROM Siparisler WHERE MusteriID = M.ID) > 50000;
Hesapla ve Ekle: Cirosu 50 bin'i geçen müşterileri hesaplayıp VIP tablosuna aktarır.

5.4 GLOBAL SENARYO: FRAUD (Dolandırıcılık) Tespiti

Subquery'lerin en sık kullanıldığı yer: Veri madenciliği ve anomali tespiti.

REPORT: ŞÜPHELİ MÜŞTERİLER
SELECT M.Ad, M.Email, ( -- Scalar Subquery: Müşterinin toplam harcaması SELECT SUM(Tutar) FROM Siparisler WHERE MusteriID = M.ID ) AS ToplamHarcama FROM Musteriler M WHERE -- 1. Kriter: Hiç siparişi olmayanlar (Potansiyel Bot) NOT EXISTS ( SELECT 1 FROM Siparisler S WHERE S.MusteriID = M.ID ) OR -- 2. Kriter: Kendi yaşadığı şehir dışından sipariş verenler (Kart Çalıntı Riski) EXISTS ( SELECT 1 FROM Siparisler S JOIN Adresler A ON S.AdresID = A.ID WHERE S.MusteriID = M.ID AND A.Sehir != M.Sehir );
06

Hafta 6: İndeksler (Indexes)

Arama hızını 100 katına çıkaran sihirli yapılar.

6.1 İndeks Tipleri ve Oluşturma

1. CLUSTERED INDEX (Fiziksel)
CREATE CLUSTERED INDEX IX_TC ON Personel(TCNo);

Veriyi diske bu sırayla yazar.

2. NON-CLUSTERED INDEX (Mantıksal)
CREATE NONCLUSTERED INDEX IX_Soyad ON Personel(Soyad);

Kitabın arkasındaki indeks gibidir.

3. UNIQUE INDEX (Benzersiz)
CREATE UNIQUE INDEX IX_Tel ON Personel(Telefon);

Aynı numaranın girilmesini engeller.

4. COMPOSITE INDEX (Çoklu)
CREATE INDEX IX_AdSoyad ON Personel(Ad, Soyad);

İki kolona göre arama yapılırsa uçar.

Ekstra İndeks Senaryoları

Tarih Sorgusu Hızlandırma
CREATE INDEX IX_SatisTarihi ON Satislar(Tarih);
Neden? "İki tarih arası rapor ver" denildiğinde (BETWEEN) çok hızlandırır.
Foreign Key İndeksleme
CREATE INDEX IX_Satis_Musteri ON Satislar(MusteriID);
Neden? JOIN işlemlerinde performansı artırır. İlişki kurulan kolonlar indekslenmeli.

6.2 İndeks Bakım ve Yönetim

Senaryo A: İndeksi Silme
DROP INDEX IX_Soyad ON Personel;
Senaryo B: İndeksi Yeniden Oluşturma (Rebuild)
ALTER INDEX ALL ON Personel REBUILD; -- Parçalanmış (fragmente) indeksleri onarır, performansı geri getirir.
İndeks Neden Kullanılır
Performans amaçlı cart curt bir şeyler eklenecek
Fillfactor nasıl çalışır
Performans amaçlı cart curt bir şeyler eklenecek
İndeks Nasıl Çalışır
Performans amaçlı cart curt bir şeyler eklenecek

6.3 İndeks Ustalık Sınıfı

INCLUDE (Kapsamlı) Index
CREATE NONCLUSTERED INDEX IX_UrunDetay ON Urunler(Ad) INCLUDE (Fiyat, Stok);
Hız Canavarı: Aramayı 'Ad' ile yapar ama Fiyat ve Stok bilgisini de indekste tutar. Tabloya (Heap/Clustered) gitmeye gerek kalmaz.
FILTERED (Filtreli) Index
CREATE INDEX IX_SadeceAktifler ON Urunler(Ad) WHERE Stok > 0;
Tasarruf: Sadece stoğu olan ürünleri indeksler. İndeks boyutu küçülür, hız artar.
FILL FACTOR (Doluluk Oranı)
CREATE INDEX IX_Musteri ON Musteriler(Ad) WITH (FILLFACTOR = 80);
Bakım: Sayfaların %80'ini doldurur, %20'sini boş bırakır. Yeni kayıt gelince sayfa bölünmesi (Page Split) olmaz.

6.4 GLOBAL SENARYO: Performans Logu (Tuning)

Bir DBA (Veritabanı Yöneticisi) bir sorguyu nasıl hızlandırır? İşte gerçek analiz süreci.

ADIM 1: SORUNLU SORGUYU BUL
SET STATISTICS IO ON; -- Okunan sayfa sayısını göster SET STATISTICS TIME ON; -- Süreyi göster SELECT * FROM Siparisler WHERE Tarih BETWEEN '2023-01-01' AND '2023-12-31'; -- ÇIKTI: Table 'Siparisler'. Scan count 1, logical reads 15000. (ÇOK YAVAŞ - Full Table Scan)
ADIM 2: İNDEKS OLUŞTUR
CREATE NONCLUSTERED INDEX IX_SiparisTarih ON Siparisler(Tarih) INCLUDE (MusteriID, Tutar); -- Sık istenen kolonları da ekle (Covering Index)
ADIM 3: TEKRAR TEST ET
SELECT MusteriID, Tutar FROM Siparisler WHERE Tarih BETWEEN '2023-01-01' AND '2023-12-31'; -- ÇIKTI: Table 'Siparisler'. Scan count 1, logical reads 50. (300 KAT HIZLI - Index Seek)
07

Hafta 7: Saklı Yordamlar (Procedures)

Veritabanını otomatize eden "Yemek Tarifleri" (Recipe Book).

7.1 Stored Procedure (Saklı Yordam) Tarifleri

Sık yapılan işleri paketleyin. Parametre verin, sonuç alın.

Tarif 1: Akıllı Arama Motoru
CREATE PROCEDURE sp_UrunAra @Kelime NVARCHAR(50), @MinFiyat DECIMAL DEFAULT 0 AS BEGIN SELECT * FROM Urunler WHERE (Ad LIKE '%' + @Kelime + '%') AND Fiyat >= @MinFiyat; END;
Tarif 2: Güvenli Bakiye Yükleme (Transaction)
CREATE PROCEDURE sp_ParaYukle @KartNo CHAR(16), @Miktar MONEY AS BEGIN BEGIN TRANSACTION; BEGIN TRY UPDATE Hesaplar SET Bakiye += @Miktar WHERE KartNo = @KartNo; INSERT INTO Loglar(Islem, Tutar) VALUES('Yükleme', @Miktar); COMMIT; -- Hata yoksa onayla END TRY BEGIN CATCH ROLLBACK; -- Hata varsa her şeyi geri al! END CATCH END;

Ekstra Prosedür Örnekleri

Müşteri Bilgisi Güncelleme
CREATE PROC sp_MusteriGuncelle @ID INT, @YeniTel VARCHAR(15) AS UPDATE Musteriler SET Telefon = @YeniTel WHERE ID = @ID;
Kategoriye Göre Ürün Sayısı
CREATE PROC sp_KategoriRaporu AS SELECT K.Ad, COUNT(U.ID) as Adet FROM Kategoriler K JOIN Urunler U ON K.ID = U.KategoriID GROUP BY K.Ad;

7.2 Trigger (Tetikleyici) Koleksiyonu

Tabloların üzerindeki görünmez bekçiler.

1. AFTER INSERT (Hoşgeldin)
CREATE TRIGGER trg_YeniUye ON Uyeler AFTER INSERT AS BEGIN -- 1. Eklenen veriyi yakala (Magic Table: inserted) DECLARE @Mail VARCHAR(50); SELECT @Mail = Email FROM inserted; -- 2. Hoşgeldin maili prosedürünü tetikle EXEC sp_MailGonder @Mail, 'Ailemize Hoşgeldin!'; END;
Senaryo: Üye kaydolduğu an (sistem saati fark etmeksizin) otomatik mail gider. CRON job gerekmez.
2. AFTER DELETE (Yedekle)
CREATE TRIGGER trg_Yedekle ON Urunler AFTER DELETE AS BEGIN -- Silinen veriyi 'Silinenler' tablosuna taşı INSERT INTO Silinenler (UrunID, Ad, SilinmeTarihi) SELECT ID, Ad, GETDATE() FROM deleted; END;

Veri kaybını önler. "Ctrl+Z" tuşu gibidir.

3. INSTEAD OF DELETE (Koruma)
CREATE TRIGGER trg_Koruma ON Personel INSTEAD OF DELETE AS BEGIN -- Güvenlik Kontrolü: Admin mi silinmeye çalışılıyor? IF EXISTS (SELECT * FROM deleted WHERE Unvan='Admin') BEGIN -- İşlemi durdur ve hata fırlat RAISERROR('Kritik Hata: Admin silinemez!', 16, 1); END ELSE BEGIN -- Admin değilse silme işlemine izin ver DELETE FROM Personel WHERE ID IN (SELECT ID FROM deleted); END END;
Güvenlik Duvarı: Kötü niyetli girişimleri veya kazaları veritabanı seviyesinde engeller.
4. AFTER UPDATE (Tarihçe)
CREATE TRIGGER trg_Tarih ON Urunler AFTER UPDATE AS BEGIN UPDATE Urunler SET Guncelleme = GETDATE() FROM Urunler U JOIN inserted I ON U.ID = I.ID; END;
5. Stok Kontrol (Validasyon)
CREATE TRIGGER trg_StokKontrol ON Urunler AFTER UPDATE AS BEGIN -- Eksi stok girişi yapıldı mı kontrol et IF EXISTS (SELECT * FROM inserted WHERE Stok < 0) BEGIN -- İşlemi iptal et (Yapılan değişikliği geri al) ROLLBACK TRANSACTION; -- Kullanıcıya hata mesajı göster RAISERROR('Hata: Stok miktarı eksiye düşemez!', 16, 1); END END;
Logic (İş Mantığı): Frontend kontrolü yetmez. Veritabanı tutarlılığı için bu kontrol şarttır.

7.3/7.4 SP & Trigger Hazine Sandığı

DDL Trigger (Tablo Silinemez!)
CREATE TRIGGER trg_Guvenlik ON DATABASE FOR DROP_TABLE AS BEGIN PRINT 'Tablo silmek yasaktır!'; ROLLBACK; END;
Admin Koruması: Veritabanında kimse yanlışlıkla veya kasten tablo silemez.
Audit Trigger (Değişiklik Logu)
CREATE TRIGGER trg_FiyatLog ON Urunler AFTER UPDATE AS BEGIN INSERT INTO FiyatGecmisi (UrunID, EskiFiyat, YeniFiyat) SELECT D.ID, D.Fiyat, I.Fiyat FROM deleted D JOIN inserted I ON D.ID = I.ID END;
Tarihçe: Ürünün fiyatı değiştiğinde eski ve yeni fiyatı log tablosuna yazar.
SP With Encryption (Şifreli Kod)
CREATE PROCEDURE sp_GizliIslem WITH ENCRYPTION AS SELECT * FROM Maaslar;
Gizlilik: Prosedürün kaynak kodunu (Source Code) kimse göremez.

7.5 GLOBAL SENARYO: Banka Para Transferi (EFT/Havale)

Bir bankacılık uygulamasının en kritik prosedürü. Hata affetmez, işlem bütünlüğü (ACID) şarttır.

STORED PROCEDURE: MoneyTransfer
CREATE PROCEDURE sp_ParaTransferi @GonderenIBAN CHAR(26), @AliciIBAN CHAR(26), @Tutar DECIMAL(18,2) AS BEGIN -- Hata yönetimi aktif SET XACT_ABORT ON; BEGIN TRANSACTION; -- İşlem Başlat BEGIN TRY -- 1. KONTROL: Gönderen bakiyesi yeterli mi? IF NOT EXISTS (SELECT 1 FROM Hesaplar WHERE IBAN = @GonderenIBAN AND Bakiye >= @Tutar) BEGIN RAISERROR('Yetersiz Bakiye!', 16, 1); END -- 2. İŞLEM: Para Çıkışı UPDATE Hesaplar SET Bakiye = Bakiye - @Tutar WHERE IBAN = @GonderenIBAN; -- 3. İŞLEM: Para Girişi UPDATE Hesaplar SET Bakiye = Bakiye + @Tutar WHERE IBAN = @AliciIBAN; -- 4. LOGLAMA: Dekont Kaydı INSERT INTO IslemLoglari (Gonderen, Alici, Tutar, Tarih) VALUES (@GonderenIBAN, @AliciIBAN, @Tutar, GETDATE()); -- Her şey yolundaysa onayla COMMIT TRANSACTION; PRINT 'Transfer Başarılı!'; END TRY BEGIN CATCH -- Bir hata olduysa her şeyi geri al (Para havaya uçmasın) ROLLBACK TRANSACTION; THROW; -- Hatayı kullanıcıya göster END CATCH END;
08

Hafta 8: Fonksiyonlar (Functions)

Kendi Excel formüllerinizi yaratın.

8.1 Scalar Function (Tek Değer) Senaryoları

Senaryo A: KDV Dahil Fiyat Hesapla
CREATE FUNCTION fn_KDVliFiyat (@Fiyat DECIMAL, @Oran DECIMAL) RETURNS DECIMAL(18,2) AS BEGIN RETURN @Fiyat * (1 + @Oran/100); END; -- Kullanım: SELECT Ad, dbo.fn_KDVliFiyat(Fiyat, 18) FROM Urunler;
Senaryo B: İsim Gizleme (KVKK)
CREATE FUNCTION fn_Yildizla (@Metin NVARCHAR(50)) RETURNS NVARCHAR(50) AS BEGIN RETURN LEFT(@Metin, 2) + '***'; END; -- Kullanım: SELECT dbo.fn_Yildizla(Ad) FROM Musteriler; -- "Ah***" çıkar.
Senaryo C: Tarih Formatlama (TR)
CREATE FUNCTION fn_TurkceTarih (@Tarih DATETIME) RETURNS NVARCHAR(50) AS BEGIN RETURN FORMAT(@Tarih, 'dd MMMM yyyy', 'tr-TR'); END; -- Sonuç: 01 Ocak 2023

8.2 Table-Valued Function (Tablo) Senaryoları

Parametre alabilen View gibi çalışır.

Senaryo D: Belirli Tarih Aralığındaki Satışlar
CREATE FUNCTION fn_SatisRaporu (@Baslangic DATETIME, @Bitis DATETIME) RETURNS TABLE AS RETURN ( SELECT * FROM Satislar WHERE Tarih BETWEEN @Baslangic AND @Bitis ); -- Kullanım: SELECT * FROM dbo.fn_SatisRaporu('2023-01-01', '2023-01-31');

8.3 Fonksiyon Ustalık Sınıfı

Yaş Hesaplama (Scalar)
CREATE FUNCTION fn_YasHesapla (@DTarih DATE) RETURNS INT AS BEGIN RETURN DATEDIFF(YEAR, @DTarih, GETDATE()); END;
Pratik: Doğum tarihini ver, yaşını al. Her raporda tekrar yazmaya gerek yok.
Schema Binding (Tabloyu Kilitleme)
CREATE FUNCTION fn_StokDeger (@UrunID INT) RETURNS MONEY WITH SCHEMABINDING AS BEGIN RETURN (SELECT Fiyat FROM dbo.Urunler WHERE ID=@UrunID) END;
Koruma: Fonksiyon varken 'Urunler' tablosunun yapısı değiştirilemez veya tablo silinemez.

8.4 GLOBAL SENARYO: Raporlama Araç Kutusu (Toolkit)

Her raporda tekrar tekrar yazılan kodları tek merkezde toplayın.

FONKSİYON 1: PARA BİRİMİ DÖNÜŞTÜRÜCÜ
CREATE FUNCTION fn_DovizCevir (@TLTutar DECIMAL, @KurTipi CHAR(3)) RETURNS DECIMAL(18,2) AS BEGIN DECLARE @Kur DECIMAL(18,4); SELECT @Kur = SatisKuru FROM Kurlar WHERE Tip = @KurTipi AND Tarih = CAST(GETDATE() AS DATE); RETURN @TLTutar / ISNULL(@Kur, 1); -- Kur bulunamazsa 1'e böl (Hata vermesin) END;
FONKSİYON 2: GÜVENLİ İSİM (MASKING)
CREATE FUNCTION fn_Maskele (@Veri NVARCHAR(100)) RETURNS NVARCHAR(100) AS BEGIN RETURN LEFT(@Veri, 1) + '***' + RIGHT(@Veri, 1); END;
KULLANIM SENARYOSU: YÖNETİCİ EKRANI
SELECT dbo.fn_Maskele(M.AdSoyad) AS Musteri, U.Ad AS Urun, dbo.fn_DovizCevir(S.Tutar, 'USD') AS DolarFiyati FROM Satislar S JOIN Musteriler M ON S.MusteriID = M.ID JOIN Urunler U ON S.UrunID = U.ID;

DEV ALTYAPI TAMAMLANDI

50'den fazla senaryo, yüzlerce satır kod örneği başarıyla yüklendi.