Aşağıdaki iş sorusu için SQL sorgusu yaz ve analiz et:
VERİTABANI:
- Tip: [MYSQL / POSTGRES / ORACLE / SQL SERVER]
- İlgili tablolar: [TABLO LİSTESİ]
- Şemalar: [ALANLAR VE TİPLERİ]
İŞ SORUSU:
[AÇıK SORU - ÖRN: "SON 12 AYIN AYLıK SATıŞ TOPLAMLARI VE BİR ÖNCEKI AYA GÖRE BÜYÜME ORANı"]
HEDEF:
- Kullanıcı: [YÖNETICİ / OPERASYONEL]
- Çıktı formatı: [TABLO / GRAFIK / DASHBOARD]
- Güncellik: [TEK SEFERLIK / HAFTALIK / GERÇEK ZAMANLI]
SQL YAKLAŞıM 10 ADIMDA:
ADIM 1 — İŞ SORUSUNU ANLAMA
1.1 SORUYU NETLEŞTIR
- Ne soruyor (spesifik olarak)
- Hangi zaman dilimi
- Hangi grup / segment
- Hangi metrikler
- Nasıl gruplamalı
- Nasıl filtrelenmeli
- Sıralama
ÖRNEKLER:
- BELIRSIZ: "Satışları göster"
- NET: "Son 12 ayda, ürün kategorisi bazında aylık satış toplamı, bir önceki aya göre % değişim, ilk 10 sıralanmış"
1.2 BUSINESS LOGIC
- Hangi durum "satış" sayılır (tamamlanmış mı, iptal dahil mi)
- Hangi tarih (sipariş tarihi mi, teslimat mı)
- Hangi tutar (net mi, vergiler dahil mi)
- İade nasıl ele alınacak
ADIM 2 — VERİ KAŞIFı (Data Exploration)
2.1 TABLO ANALIZI
- Kaç satır var
- Örnek veri (ilk 5 satır)
- NULL değerler
- Unique değerler (GROUP BY için)
- Min/max tarih
- Min/max değerler
ÖRNEK:
```sql
-- Tablo boyutu
SELECT COUNT(*) AS toplam_satir FROM siparisler;
-- Tarih aralığı
SELECT MIN(siparis_tarihi), MAX(siparis_tarihi) FROM siparisler;
-- Durum dağılımı
SELECT durum, COUNT(*) FROM siparisler GROUP BY durum;
-- NULL kontrolü
SELECT COUNT(*) - COUNT(toplam_tutar) AS null_tutar FROM siparisler;
```
2.2 İLIŞKI ANALIZI
- Tablolar nasıl bağlı
- Join tipi (INNER, LEFT, RIGHT)
- Cardinality (1:1, 1:N, M:N)
- Referans bütünlüğü
ADIM 3 — SORGU TASARıMı
3.1 BASİT BAŞLA
- Önce küçük bir sonuç al
- Karmaşıklığı aşamalı ekle
- Her adımda doğrula
3.2 CTE (Common Table Expression)
- Karmaşık sorguyu bölmeyi kolaylaştırır
- Okunabilirlik yüksek
- Subquery'den daha net
```sql
WITH aylik_satis AS (
SELECT
DATE_FORMAT(siparis_tarihi, '%Y-%m') AS ay,
SUM(toplam_tutar) AS satis_toplam
FROM siparisler
WHERE durum = 'tamamlandi'
AND siparis_tarihi >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(siparis_tarihi, '%Y-%m')
),
aylik_karsilastirma AS (
SELECT
ay,
satis_toplam,
LAG(satis_toplam) OVER (ORDER BY ay) AS onceki_ay
FROM aylik_satis
)
SELECT
ay,
satis_toplam,
onceki_ay,
ROUND(((satis_toplam - onceki_ay) / onceki_ay) * 100, 2) AS buyume_orani
FROM aylik_karsilastirma
ORDER BY ay;
```
3.3 WINDOW FUNCTIONS
- ROW_NUMBER(), RANK(), DENSE_RANK()
- LAG(), LEAD() - önceki/sonraki satır
- SUM, AVG OVER (sliding window)
- PARTITION BY - segment içinde
ÖRNEK - HER MÜŞTERININ SON SIPARIŞI:
```sql
WITH siparis_sirali AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY musteri_id ORDER BY siparis_tarihi DESC) AS sira
FROM siparisler
)
SELECT *
FROM siparis_sirali
WHERE sira = 1;
```
3.4 AGGREGATE FONKSIYONLAR
- COUNT, SUM, AVG, MIN, MAX
- STDDEV, VARIANCE
- PERCENTILE_CONT (median)
- GROUP BY with HAVING
ADIM 4 — PERFORMANS
4.1 INDEX KULLANIMI
INDEX KONTROLÜ:
- WHERE koşulu alanları
- JOIN anahtarları
- ORDER BY alanları
EXPLAIN KULLAN:
```sql
EXPLAIN SELECT ...;
```
- Full table scan var mı
- Index kullanılıyor mu
- Sıralama / gruplama memory'de mi
4.2 SORGU OPTİMİZASYONU
- SELECT * yerine spesifik sütunlar
- WHERE önce filtrele, sonra aggregate
- HAVING sadece aggregate filtrelerinde
- DISTINCT kullanımı minimum
- Subquery yerine JOIN (çoğu zaman)
- UNION ALL > UNION (duplicates varsa sorun değil)
4.3 BÜYÜK VERI
- LIMIT ile sonuç sınırla
- Önce aggregate
- Partition kullan (tarih bazlı)
- Materialized view
- Cache stratejisi
ADIM 5 — YAYGıN SORGULAR
5.1 TIME-SERIES ANALIZ
```sql
-- Haftalık trend
SELECT
DATE_FORMAT(siparis_tarihi, '%Y-%u') AS hafta,
COUNT(*) AS siparis_sayisi,
SUM(toplam_tutar) AS ciro,
AVG(toplam_tutar) AS ortalama_tutar
FROM siparisler
GROUP BY DATE_FORMAT(siparis_tarihi, '%Y-%u')
ORDER BY hafta;
```
5.2 COHORT ANALIZI
```sql
WITH ilk_siparis AS (
SELECT musteri_id, MIN(siparis_tarihi) AS ilk_siparis_tarihi
FROM siparisler
GROUP BY musteri_id
),
cohort AS (
SELECT
musteri_id,
DATE_FORMAT(ilk_siparis_tarihi, '%Y-%m') AS cohort_ay
FROM ilk_siparis
)
SELECT
c.cohort_ay,
COUNT(DISTINCT c.musteri_id) AS musteri_sayisi,
SUM(s.toplam_tutar) AS ciro
FROM cohort c
JOIN siparisler s ON c.musteri_id = s.musteri_id
GROUP BY c.cohort_ay;
```
5.3 RFM SEGMENTASYONU (müşteri segment)
```sql
WITH rfm AS (
SELECT
musteri_id,
DATEDIFF(CURRENT_DATE, MAX(siparis_tarihi)) AS recency,
COUNT(*) AS frequency,
SUM(toplam_tutar) AS monetary
FROM siparisler
WHERE durum = 'tamamlandi'
GROUP BY musteri_id
),
rfm_skor AS (
SELECT *,
NTILE(5) OVER (ORDER BY recency DESC) AS r_skor,
NTILE(5) OVER (ORDER BY frequency) AS f_skor,
NTILE(5) OVER (ORDER BY monetary) AS m_skor
FROM rfm
)
SELECT
CASE
WHEN r_skor >= 4 AND f_skor >= 4 THEN 'Champion'
WHEN r_skor >= 3 AND f_skor >= 3 THEN 'Loyal'
WHEN r_skor = 5 AND f_skor <= 2 THEN 'New Customer'
WHEN r_skor <= 2 AND f_skor >= 3 THEN 'At Risk'
ELSE 'Other'
END AS segment,
COUNT(*) AS musteri_sayisi
FROM rfm_skor
GROUP BY segment;
```
5.4 FUNNEL ANALİZİ
```sql
SELECT
COUNT(DISTINCT CASE WHEN adim = 'ziyaret' THEN user_id END) AS ziyaret,
COUNT(DISTINCT CASE WHEN adim = 'sepete_ekle' THEN user_id END) AS sepete_ekle,
COUNT(DISTINCT CASE WHEN adim = 'odeme_basladi' THEN user_id END) AS odeme_basladi,
COUNT(DISTINCT CASE WHEN adim = 'siparis' THEN user_id END) AS siparis
FROM kullanici_olaylari
WHERE olay_tarihi BETWEEN '2024-01-01' AND '2024-12-31';
```
5.5 PIVOT
```sql
SELECT
ay,
SUM(CASE WHEN kategori = 'Elektronik' THEN tutar ELSE 0 END) AS elektronik,
SUM(CASE WHEN kategori = 'Giyim' THEN tutar ELSE 0 END) AS giyim,
SUM(CASE WHEN kategori = 'Gıda' THEN tutar ELSE 0 END) AS gida
FROM satislar
GROUP BY ay;
```
ADIM 6 — DATA QUALITY
6.1 NULL YÖNETIMI
- COALESCE(sütun, 'varsayılan')
- ISNULL (SQL Server)
- NULLIF (sıfırdan koru)
- IS NULL / IS NOT NULL
6.2 DUPLICATE TESPITI
```sql
SELECT ad, soyad, email, COUNT(*) as tekrar
FROM musteriler
GROUP BY ad, soyad, email
HAVING COUNT(*) > 1;
```
6.3 OUTLIER TESPITI
```sql
WITH istatistik AS (
SELECT
AVG(tutar) AS ortalama,
STDDEV(tutar) AS std_sapma
FROM siparisler
)
SELECT s.*
FROM siparisler s, istatistik i
WHERE ABS(s.tutar - i.ortalama) > 3 * i.std_sapma;
```
ADIM 7 — RAPORLAMA ÇıKTıSı
7.1 EXECUTIVE DASHBOARD
- Yüksek seviye KPI
- Trend grafikleri
- Segment karşılaştırma
- Otomatik yenileme
7.2 OPERASYONEL RAPOR
- Detay listeler
- Filtreleme
- Drill-down
- Excel export
7.3 AD HOC ANALİZ
- Belirli soruya cevap
- Tek seferlik
- Hızlı sonuç
ADIM 8 — VERSIYONLAMA
- Sorgu dosyalarını git'te tut
- Yorum satırı açıklama
- Değişiklik nedeni
- Örnek çıktı ekle
ADIM 9 — DAĞıTıM
KANALLAR:
- BI araçları (Tableau, Power BI, Looker)
- E-posta raporu (otomatik)
- Slack / Teams notification
- Excel (manual)
- API endpoint
ADIM 10 — YORUMLAMA
10.1 VERI → İNSIGHT
SADECE SAYI DEĞIL:
- Trend (artıyor/azalıyor)
- Anomali (beklenmedik)
- Neden (root cause)
- Etki (ne anlama geliyor)
- Aksiyon (ne yapılmalı)
10.2 RECOMMENDATION
- Veriyi karar yap
- 3 öneri (veri bazlı)
- Beklenen etki
- Uygulanabilirlik
- Risk
Türkçe açıklama + sorgular İngilizce (SQL). İş analizi best practice'lerine uygun.