SQL SORGUSU

SQL veritabanı silindiri üzerinde büyüteç merceği ile sorgu arama eylemini simgeleyen kompozisyon

Bir raporun arkasındaki "bu sayıyı nereden aldın" sorusuna cevap genellikle iki kelimeden oluşur: SQL sorgu. Excel'de pivot tablo, Power BI'da DAX ölçü, dashboard'larda KPI — hemen hepsinin altında veriyi seçen, filtreleyen, gruplayan bir SQL ifadesi durur.

Veriyle çalışan bir profesyonel için SQL bilmek, raporları sorgulayabilmek ve "şu kırılımı bir de bu açıdan göster" diyebilmek demektir. Yazılım geliştirici olmaya gerek yok; iş analisti, raportör, finans uzmanı veya ürün müdürü de SQL'le çalışabilir.

SQL Sorgu Nedir?

SQL (Structured Query Language), ilişkisel veritabanlarıyla konuşma dilidir. Bir sorgu, "şu tablodan, şu koşulu sağlayan satırları, şu sırada getir" diyen bir cümleden ibaret. Bu cümlenin yapısı bütün ilişkisel veritabanlarında benzer: PostgreSQL, MySQL, SQL Server, Oracle, Snowflake hepsi aynı temel dilbilgisini paylaşır.

SQL'in gücü, veriye dilbilgisinin verdiği kolaylıkla erişilmesi. "Geçen ayın ilk on müşterisi" gibi bir cümleyi neredeyse Türkçe sözle ifade eder gibi yazabilirsiniz.

Bir Sorgunun Yapı Taşları

Bir SQL sorgusu sabit bir sıralamayla okunur. Yazılırken bu sıra şu şekilde durur:

SELECT -- hangi kolonlar gösterilecek
FROM -- hangi tablodan
WHERE -- hangi koşullar sağlanacak
GROUP BY -- nasıl gruplanacak
HAVING -- grup sonrası filtre
ORDER BY -- sonuç nasıl sıralanacak
LIMIT -- kaç satır gösterilecek

Aslında veritabanı bu satırları yukarıdan aşağı çalıştırmıyor — önce FROM, sonra WHERE, GROUP BY, HAVING, SELECT ve en son ORDER BY işliyor. Bu mantığı bilmek HAVING ve WHERE arasındaki farkı anlamayı kolaylaştırır.

Basit Bir Örnek

"Geçen ay tamamlanan siparişleri, en yüksek tutardan en düşüğe sırala" cümlesini SQL'e dökelim:

SELECT
 siparis_id,
 musteri_id,
 tarih,
 tutar
FROM siparisler
WHERE durum = 'Tamamlandi'
 AND tarih >= '2026-04-01'
 AND tarih < '2026-05-01'
ORDER BY tutar DESC
LIMIT 100;

Bu sorguda dikkat edilmesi gereken birkaç nokta var. SELECT * yazmak yerine ihtiyaç duyulan kolonların adını yazmak hem performans hem okunabilirlik açısından doğru tercih. Tarih filtresinde BETWEEN yerine yarı açık aralık (>= ve <) kullanmak ay sonu saat 23:59:59'a takılmamak için pratik.

JOIN: İki Tablodan Tek Sonuç

Gerçek veritabanlarında bilgi tek tabloda durmaz. Siparişler tablosunda müşteri kimliği var, ad-soyad ise müşteri tablosunda. İkisini birleştirmek için JOIN kullanılır.

SELECT
 s.siparis_id,
 s.tarih,
 m.unvan,
 s.tutar
FROM siparisler s
INNER JOIN musteriler m ON m.id = s.musteri_id
WHERE s.durum = 'Tamamlandi'
ORDER BY s.tarih DESC;

INNER JOIN sadece iki tarafta eşleşen kayıtları getirir. Konunun resmi ve ayrıntılı anlatımı Wikipedia'nın SQL sayfasında bulunur. Müşteri tablosunda olmayan bir kayıt veya sipariş veren bir müşterisi olmayan kayıt sonuçta görünmez. LEFT JOIN ise soldaki tablonun tüm kayıtlarını korur — eşleşme yoksa sağ taraftaki kolonlar NULL döner.

LEFT JOIN'in klasik kullanımı "müşterisi olup sipariş vermemiş" gibi sorularda görülür:

SELECT m.unvan
FROM musteriler m
LEFT JOIN siparisler s ON s.musteri_id = m.id
WHERE s.siparis_id IS NULL;
GROUP BY ile kanal bazında aylık ciro özetini sunan Türkçe başlıklı SQL sonuç tablosu

GROUP BY: Toplam, Adet, Ortalama

Tek tek satırları görmek yerine özet sonuç istediğimizde GROUP BY devreye girer. "Her kanalın aylık toplam cirosu" sorusunun cevabı şöyle yazılır:

SELECT
 kanal,
 DATE_TRUNC('month', tarih) AS ay,
 COUNT(*) AS siparis_sayisi,
 SUM(tutar) AS toplam_ciro,
 AVG(tutar) AS ortalama_sepet
FROM siparisler
WHERE durum = 'Tamamlandi'
GROUP BY kanal, DATE_TRUNC('month', tarih)
ORDER BY ay, toplam_ciro DESC;

GROUP BY ile birlikte kullanılan SUM, COUNT, AVG, MIN, MAX fonksiyonları "aggregate" denilen özetleme fonksiyonlarıdır. Bir SELECT cümlesinde aggregate olmayan her kolon GROUP BY listesinde de yer almalı — yoksa hata alırsınız.

WHERE ve HAVING Farkı

WHERE satır seviyesinde filtreleme yapar — gruplamadan önce çalışır. HAVING ise grupları filtreler. "Toplam cirosu 50 binin üstünde olan kanallar" sorusunun cevabı HAVING ile yazılır:

SELECT
 kanal,
 SUM(tutar) AS toplam
FROM siparisler
WHERE durum = 'Tamamlandi'
GROUP BY kanal
HAVING SUM(tutar) > 50000
ORDER BY toplam DESC;

WHERE içine SUM yazmaya çalışmak hata verir; çünkü WHERE çalıştığında henüz toplam hesaplanmamıştır. Bu sıralama hatası SQL'i yeni öğrenenlerde en sık takılan noktalardan biri.

Yaygın Tuzaklar

SQL pratikte birkaç tipik tuzakla karşılaşır. Bunları bilmek hata ayıklama süresini kısaltır:

  • NULL karşılaştırması: = NULL hep yanlış döner. Doğrusu IS NULL kullanmak.
  • JOIN'de WHERE filtresi: LEFT JOIN sonrası sağ taraf kolonunda WHERE koşulu INNER JOIN gibi davranır. Filtreyi ON koşuluna almak gerekir.
  • SELECT * üretim sorgusunda: Tablo yapısı değişince rapor sessizce kırılır; kolon adlarını açıkça yazmak güvenli.
  • Tarih filtresinde fonksiyon: WHERE YEAR(tarih) = 2026 indeksten yararlanamaz; yarı açık aralık kullanmak performansı kurtarır.
  • AND/OR parantezi: Karışık koşullarda parantez kullanmadan yazılan filtreler beklenmedik sonuç verir.

Nereden Başlamalı?

SQL öğrenmenin en hızlı yolu, gerçek bir tabloya sorgu yazmaktan geçer. PostgreSQL'in ücretsiz demo veritabanları, SQLite tek dosya kurulumları veya basit bir CSV'yi DuckDB'ye yükleyip sorgulamak da hızlı başlangıçlar.

İlk hafta SELECT-WHERE-ORDER BY ile rahat ettikten sonra JOIN ve GROUP BY ikilisi geliyor. Bu iki yapı oturduğunda iş hayatında karşılaşılan raporlama sorularının %70-80'i çözülebilir hale geliyor. Bu temeli sıkı kuran bir SQL eğitim programı, yanına veri analizi ve raporlama içeriği eklendiğinde rapor odaklı pratik senaryolar üzerinden ilerler.

Sıkça Sorulan Sorular

SQL öğrenmek için programlama bilgisi gerekiyor mu?

Hayır. SQL bir programlama dili değil sorgu dili; algoritma kurma değil veriyi tarif etme üzerine kurulu. Excel'de pivot tablo kullanabilen herkes birkaç hafta içinde temel SQL'i öğrenebilir. Yazılım geliştirme bilmek avantaj sağlar ama gerek değil.

INNER JOIN ile LEFT JOIN arasındaki fark nedir?

INNER JOIN sadece iki tabloda da eşleşen kayıtları getirir. LEFT JOIN soldaki tablonun tüm kayıtlarını korur, sağdaki tablodan eşleşme yoksa o kolonlara NULL koyar. 'Siparişi olmayan müşterileri bul' gibi sorular LEFT JOIN ile çözülür.

WHERE ve HAVING arasında nasıl seçim yapılır?

WHERE satır seviyesinde, GROUP BY'dan önce filtreleme yapar. HAVING ise gruplandıktan sonra grup düzeyinde filtreleme yapar. Bir aggregate fonksiyon (SUM, COUNT, AVG gibi) sonucuna göre filtreleme yapacaksanız HAVING; tek satır bilgisine göre filtreleme yapacaksanız WHERE kullanın.

SELECT * neden tavsiye edilmiyor?

Tablo yapısı değiştiğinde sorgu sessizce kırılabilir veya beklenmedik kolon getirir. Ayrıca gereksiz veri çekildiği için ağ ve bellek maliyeti artar. Üretim sorgularında ihtiyaç duyulan kolonları tek tek listelemek hem performans hem bakım açısından güvenli.

NULL değer karşılaştırması neden farklı?

SQL'de NULL bilinmeyen anlamına gelir; eşitlik karşılaştırması mantıken yanıtsızdır. Bu yüzden 'sutun = NULL' her zaman yanlış döner. Doğru yöntem 'sutun IS NULL' veya 'sutun IS NOT NULL' kullanmak. UNIQUE kısıtları ve agregasyonlar da NULL'u farklı ele alır.

Tarih aralığını filtrelemek için BETWEEN mi daha iyi?

BETWEEN inclusive davranır ve sınır günündeki saat bileşeni karışıklık yaratabilir. Yarı açık aralık ('tarih &gt;= 2026-04-01 AND tarih &lt; 2026-05-01') ay sonu saat 23:59:59 sorununu önler ve indekslerden daha iyi yararlanır.

SQL sorguları hangi veritabanı motorlarında çalışır?

Temel SQL söz dizimi PostgreSQL, MySQL, SQL Server, Oracle, SQLite, Snowflake, BigQuery, DuckDB gibi neredeyse tüm ilişkisel veritabanlarında benzer çalışır. Tarih fonksiyonları, pencere fonksiyonları ve bazı özel söz dizimleri motorlar arasında farklılaşabilir.