SQL nedir?
SQL (Structured Query Language), ilişkisel veritabanlarında veriyi tanımlamak, sorgulamak ve değiştirmek için kullanılan standart dildir. Tablolar ve aralarındaki ilişkiler üzerinde çalışır; kayıtları filtreleme, birleştirme, özetleme, ekleme, güncelleme ve silme gibi işlemleri ifade eder. Bu sayfa, temel SQL komutlarını PostgreSQL örnekleriyle hızlıca hatırlamak için hazırlanmıştır.
İçindekiler / Hızlı Bakış
DQL: SELECT · DISTINCT · WHERE · ORDER BY · LIMIT / OFFSET
İlişkisel / Gelişmiş: JOIN · Alt sorgu (SUBQUERY) · CTE (WITH) · CASE WHEN · UNION / UNION ALL · INTERSECT / EXCEPT · Toplama fonksiyonları · GROUP BY / HAVING · Pencere fonksiyonları
DDL: CREATE · ALTER · DROP · TRUNCATE · PRIMARY KEY / FOREIGN KEY · CHECK / DEFAULT / NOT NULL · VIEW · INDEX · TRIGGER
TCL: BEGIN / COMMIT / ROLLBACK
DCL: GRANT / REVOKE
DQL — Veri Sorgulama
Data Query Language: veriyi okumaya yönelik ifadeler.
SELECT
Döndürülecek sütunları belirler. Sütuna veya tabloya geçici bir ad vermek için AS kullanılır (alias).
Veri:
musteriler(id INTEGER, ad TEXT)
1 | Ayşe
2 | Mehmet
Sorgu:
SELECT ad
FROM musteriler
ORDER BY id;
Sonuç:
ad
Ayşe
Mehmet
Alias örneği:
SELECT m.ad AS musteri_adi FROM musteriler AS m;
-- Sonuç: musteri_adi → Ayşe
DISTINCT
Seçilen alanların oluşturduğu tekrar eden sonuç satırlarını kaldırır.
Veri:
musteriler(id INTEGER, sehir TEXT)
1 | Ankara
2 | Ankara
3 | İstanbul
Sorgu:
SELECT DISTINCT sehir
FROM musteriler
ORDER BY sehir;
Sonuç:
sehir
Ankara
İstanbul
WHERE
Koşula uyan satırları seçer.
Veri:
musteriler(id INTEGER, ad TEXT, sehir TEXT)
1 | Ayşe | İstanbul
2 | Mehmet | Ankara
Sorgu:
SELECT ad
FROM musteriler
WHERE sehir = 'İstanbul';
Sonuç:
ad
Ayşe
ORDER BY
Sonuçları artan (ASC) veya azalan (DESC) sıraya dizer.
Veri:
siparisler(id INTEGER, tutar INTEGER)
1 | 300
2 | 900
Sorgu:
SELECT id, tutar
FROM siparisler
ORDER BY tutar DESC, id;
Sonuç:
id | tutar
2 | 900
1 | 300
LIMIT / OFFSET
Sonuç sayısını sınırlar ve başlangıçtan satır atlar.
Veri:
siparisler(id INTEGER, tutar INTEGER)
1 | 300
2 | 900
3 | 600
Sorgu:
SELECT id, tutar
FROM siparisler
ORDER BY id
LIMIT 2 OFFSET 1;
Sonuç:
id | tutar
2 | 900
3 | 600
Not: İlk satır atlanır. Kararlı sayfalama için benzersiz bir sıralama kullan.
İlişkiler ve Gelişmiş Sorgular
Tabloları birleştirme, ara sonuç üretme ve hesaplama yapıları.
JOIN
Tabloları belirtilen koşula göre birleştirir.
Veri:
musteriler(id INTEGER, ad TEXT)
1 | Ayşe
2 | Mehmet
siparisler(id INTEGER, musteri_id INTEGER)
10 | 1
Sorgu:
SELECT m.ad, s.id AS siparis_no
FROM musteriler AS m
LEFT JOIN siparisler AS s ON s.musteri_id = m.id
ORDER BY m.id;
Sonuç:
ad | siparis_no
Ayşe | 10
Mehmet | NULL
Not: LEFT JOIN soldaki eşleşmeyen satırları da korur; INNER JOIN yalnızca eşleşenleri getirir.
Alt sorgu (SUBQUERY)
Bir sorgunun sonucunu başka bir sorgunun içinde kullanır.
Veri:
siparisler(id INTEGER, tutar INTEGER)
1 | 300
2 | 900
Sorgu:
SELECT id, tutar
FROM siparisler
WHERE tutar > (SELECT AVG(tutar) FROM siparisler);
Sonuç:
id | tutar
2 | 900
Not: Ortalama 600’dür; yalnızca ortalamanın üzerindeki sipariş gelir.
CTE (WITH)
Sorgu süresince kullanılacak bir ara sonucu isimlendirir.
Veri:
siparisler(id INTEGER, musteri_id INTEGER, tutar INTEGER)
1 | 1 | 300
2 | 1 | 900
3 | 2 | 200
Sorgu:
WITH toplamlar AS (
SELECT musteri_id, SUM(tutar) AS toplam
FROM siparisler
GROUP BY musteri_id
)
SELECT musteri_id, toplam
FROM toplamlar
WHERE toplam > 1000;
Sonuç:
musteri_id | toplam
1 | 1200
CASE WHEN
Koşula göre bir sonuç değeri üretir.
Veri:
siparisler(id INTEGER, tutar INTEGER)
1 | 300
2 | 900
Sorgu:
SELECT id,
CASE WHEN tutar >= 500 THEN 'Yüksek'
ELSE 'Standart' END AS seviye
FROM siparisler
ORDER BY id;
Sonuç:
id | seviye
1 | Standart
2 | Yüksek
UNION / UNION ALL
Uyumlu sorgu sonuçlarını alt alta birleştirir.
Veri:
aktif_musteriler(id INTEGER): 1, 2
eski_musteriler(id INTEGER): 2, 3
Sorgu:
SELECT id FROM aktif_musteriler
UNION
SELECT id FROM eski_musteriler
ORDER BY id;
Sonuç:
id
1
2
3
Not: UNION tekrarları kaldırır. Aynı sorguda UNION ALL kullanılırsa sonuç 1, 2, 2, 3 olur.
INTERSECT / EXCEPT
Ortak satırları veya ilk kümede olup ikincide olmayanları bulur.
Veri:
aktif_musteriler(id INTEGER): 1, 2
eski_musteriler(id INTEGER): 2, 3
Sorgu:
SELECT id FROM aktif_musteriler
INTERSECT
SELECT id FROM eski_musteriler;
Sonuç:
id
2
Not: INTERSECT yerine EXCEPT kullanılırsa sonuç 1 olur.
Toplama fonksiyonları (COUNT / SUM / AVG / MIN / MAX)
Birden fazla satırdan tek bir özet sonuç üretir.
Veri:
siparisler(id INTEGER, tutar INTEGER)
1 | 300
2 | 900
Sorgu:
SELECT COUNT(*) AS adet,
SUM(tutar) AS toplam,
AVG(tutar) AS ortalama
FROM siparisler;
Sonuç:
adet | toplam | ortalama
2 | 1200 | 600
Not: Ortalamanın ondalık gösterimi istemciye göre değişebilir. MIN ve MAX en küçük ve en büyük değeri bulur.
GROUP BY / HAVING
GROUP BY aynı değere sahip satırları gruplar; HAVING gruplama sonrasında grupları filtreler (WHERE satırları, HAVING grupları filtreler).
Veri:
siparisler(id INTEGER, musteri_id INTEGER)
1 | 1
2 | 1
3 | 2
Sorgu (GROUP BY):
SELECT musteri_id, COUNT(*) AS adet
FROM siparisler
GROUP BY musteri_id
ORDER BY musteri_id;
Sonuç:
musteri_id | adet
1 | 2
2 | 1
Sorgu (HAVING ile filtreleme):
SELECT musteri_id, COUNT(*) AS adet
FROM siparisler
GROUP BY musteri_id
HAVING COUNT(*) >= 2;
Sonuç:
musteri_id | adet
1 | 2
Pencere fonksiyonları (OVER / PARTITION BY)
Satırları koruyarak sıra veya kümülatif toplam hesaplar.
Veri:
siparisler(id INTEGER, musteri_id INTEGER, tutar INTEGER)
1 | 1 | 300
2 | 1 | 900
3 | 2 | 200
Sorgu:
SELECT id, musteri_id,
SUM(tutar) OVER (
PARTITION BY musteri_id ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS biriken_tutar
FROM siparisler
ORDER BY id;
Sonuç:
id | musteri_id | biriken_tutar
1 | 1 | 300
2 | 1 | 1200
3 | 2 | 200
DML — Veri Değiştirme
Data Manipulation Language: kayıt ekleme, değiştirme ve silme.
INSERT
Tabloya yeni kayıt ekler. RETURNING, eklenen satırın belirtilen alanlarını döndürür.
Veri:
musteriler(id INTEGER PRIMARY KEY, ad TEXT)
Başlangıçta boş.
Sorgu:
INSERT INTO musteriler (id, ad)
VALUES (1, 'Ayşe')
RETURNING id, ad;
Sonuç:
id | ad
1 | Ayşe
UPDATE
Koşula uyan kayıtların belirtilen alanlarını değiştirir.
Veri:
musteriler(id INTEGER PRIMARY KEY, ad TEXT, sehir TEXT)
1 | Ayşe | İstanbul
Sorgu:
UPDATE musteriler
SET sehir = 'Ankara'
WHERE id = 1
RETURNING id, sehir;
Sonuç:
id | sehir
1 | Ankara
Not: WHERE kullanılmazsa tüm satırlar güncellenir.
DELETE
Koşula uyan satırları siler; tablo yapısını korur.
Veri:
siparisler(id INTEGER PRIMARY KEY, tutar INTEGER)
1 | 300
2 | 900
Sorgu:
DELETE FROM siparisler
WHERE id = 2
RETURNING id, tutar;
Sonuç:
id | tutar
2 | 900
Not: Çıktı silinen satırdır; tabloda yalnızca 1 numaralı sipariş kalır. WHERE olmazsa tüm satırlar silinir.
DDL — Yapı Tanımlama
Data Definition Language: tablo ve diğer veritabanı nesnelerini yönetir.
CREATE
Yeni bir veritabanı nesnesi oluşturur.
Veri: kategoriler tablosu henüz yok. Hedef: kategoriler(id INTEGER PRIMARY KEY, ad TEXT NOT NULL)
Sorgu:
CREATE TABLE kategoriler (
id INTEGER PRIMARY KEY,
ad TEXT NOT NULL
);
Sonuç:
CREATE TABLE
Not: Belirtilen sütunlarla boş bir tablo oluşur.
ALTER
Mevcut tablonun yapısını değiştirir.
Veri:
musteriler(id INTEGER PRIMARY KEY, ad TEXT)
1 | Ayşe
Sorgu:
ALTER TABLE musteriler ADD COLUMN telefon TEXT;
SELECT id, ad, telefon FROM musteriler;
Sonuç:
id | ad | telefon
1 | Ayşe | NULL
DROP
Nesneyi verileriyle birlikte kaldırır.
Veri:
kategoriler(id INTEGER PRIMARY KEY, ad TEXT)
1 | Kitap
Tabloya bağımlı başka nesne yok.
Sorgu:
DROP TABLE kategoriler;
Sonuç:
DROP TABLE
Not: Tablo artık yoktur; yeniden sorgulamak hata verir.
TRUNCATE
Tablodaki tüm satırları kaldırır; tablo yapısını korur.
Veri:
siparisler(id INTEGER PRIMARY KEY, tutar INTEGER)
1 | 300
2 | 900
Tabloyu referans alan foreign key yok.
Sorgu:
TRUNCATE TABLE siparisler;
SELECT COUNT(*) AS kalan_satir FROM siparisler;
Sonuç:
kalan_satir
0
Not: WHERE kabul etmez. PostgreSQL’de açık transaction içinde geri alınabilir.
PRIMARY KEY / FOREIGN KEY
Primary key benzersiz kimlik sağlar; foreign key var olan bir kayda bağlar.
Veri:
musteriler(id INTEGER PRIMARY KEY)
1
Hedef: adresler(id INTEGER PRIMARY KEY,
musteri_id INTEGER NOT NULL REFERENCES musteriler(id))
Sorgu:
CREATE TABLE adresler (
id INTEGER PRIMARY KEY,
musteri_id INTEGER NOT NULL REFERENCES musteriler(id)
);
INSERT INTO adresler (id, musteri_id)
VALUES (10, 1)
RETURNING id, musteri_id;
Sonuç:
id | musteri_id
10 | 1
Not: Olmayan bir müşteri kimliği foreign key hatası verir. Primary key tekrar veya NULL kabul etmez.
CHECK / DEFAULT / NOT NULL
CHECK koşulu sınar, DEFAULT değer sağlar, NOT NULL eksik değeri engeller.
Veri:
urunler tablosu henüz yok.
Hedef: urunler(id INTEGER PRIMARY KEY, fiyat INTEGER NOT NULL,
stok INTEGER NOT NULL DEFAULT 0)
Kural: fiyat ve stok negatif olamaz.
Sorgu:
CREATE TABLE urunler (
id INTEGER PRIMARY KEY,
fiyat INTEGER NOT NULL CHECK (fiyat >= 0),
stok INTEGER NOT NULL DEFAULT 0 CHECK (stok >= 0)
);
INSERT INTO urunler (id, fiyat)
VALUES (1, 100)
RETURNING id, fiyat, stok;
Sonuç:
id | fiyat | stok
1 | 100 | 0
Not: DEFAULT, açıkça verilen NULL değerini değiştirmez.
VIEW
Bir sorguyu sanal tablo adıyla tekrar kullanılabilir hale getirir.
Veri:
siparisler(id INTEGER, durum TEXT)
1 | gonderildi
2 | hazirlaniyor
View henüz yok.
Sorgu:
CREATE VIEW gonderilen_siparisler AS
SELECT id FROM siparisler WHERE durum = 'gonderildi';
SELECT id FROM gonderilen_siparisler;
Sonuç:
id
1
Not: Normal view, sonuçların ayrı bir kopyasını saklamaz.
INDEX
Belirli arama ve birleştirme işlemlerini hızlandırabilecek bir dizin oluşturur.
Veri:
siparisler(id INTEGER, musteri_id INTEGER)
1 | 10
2 | 20
İndeks henüz yok.
Sorgu:
CREATE INDEX idx_siparisler_musteri
ON siparisler (musteri_id);
Sonuç:
CREATE INDEX
Not: Satırlar değişmez. İndeks disk ve yazma maliyeti getirir; kullanılacağını sorgu planlayıcısı belirler.
TRIGGER
Bir veri olayında tanımlı işlemi otomatik çalıştırır.
Veri:
musteriler(id INTEGER PRIMARY KEY, ad TEXT)
Başlangıçta boş; aşağıdaki fonksiyon ve trigger henüz yok.
Sorgu:
CREATE FUNCTION adi_temizle() RETURNS trigger
LANGUAGE plpgsql AS $$
BEGIN
NEW.ad := trim(NEW.ad);
RETURN NEW;
END;
$$;
CREATE TRIGGER musteri_adi_temizligi
BEFORE INSERT ON musteriler
FOR EACH ROW EXECUTE FUNCTION adi_temizle();
INSERT INTO musteriler (id, ad)
VALUES (1, ' Ayşe ')
RETURNING id, ad;
Sonuç:
id | ad
1 | Ayşe
Not: PostgreSQL örneğidir. Adın başındaki ve sonundaki boşluklar kayıt eklenmeden temizlenir.
TCL — İşlem Yönetimi
Transaction Control Language: değişiklikleri bir transaction içinde birlikte yönetir.
BEGIN / COMMIT / ROLLBACK
BEGIN bir transaction başlatır. İçindeki değişiklikler COMMIT ile kalıcı hale gelir, ROLLBACK ile geri alınır.
Örnek 1 — COMMIT değişikliği kalıcılaştırır
Veri: musteriler(id INTEGER PRIMARY KEY, sehir TEXT) → 1 | İstanbul
BEGIN;
UPDATE musteriler SET sehir = 'Ankara' WHERE id = 1;
COMMIT;
SELECT sehir FROM musteriler WHERE id = 1;
Sonuç: sehir → Ankara
Not: Commit sonrasında ROLLBACK bu değişikliği geri almaz.
Örnek 2 — ROLLBACK değişikliği geri alır
Veri: siparisler(id INTEGER PRIMARY KEY, tutar INTEGER) → 1 | 300
BEGIN;
DELETE FROM siparisler WHERE id = 1;
ROLLBACK;
SELECT id, tutar FROM siparisler;
Sonuç: id: 1, tutar: 300 (kayıt hâlâ duruyor; silme işlenmedi)
DCL — Yetki Yönetimi
Data Control Language: kullanıcı ve rollerin nesne erişimini yönetir.
GRANT / REVOKE
GRANT bir role nesne üzerinde yetki verir; REVOKE daha önce verilmiş bir yetkiyi kaldırır.
Veri: siparisler(id INTEGER, tutar INTEGER), rol: raporlama_rolu (oturum tablo sahibi; rolün başlangıçta okuma yetkisi yok).
GRANT SELECT ON siparisler TO raporlama_rolu;
-- Sonuç: GRANT
REVOKE SELECT ON siparisler FROM raporlama_rolu;
-- Sonuç: REVOKE
Not: GRANT sonrası bağlantı/şema erişimi ayrıca gerekebilir. REVOKE sonrası, başka rol üyeliklerinden veya PUBLIC’ten gelen erişim sürebilir.
Kısa Notlar
- NULL kontrolü için
IS NULLkullanılır;NULL, boş metin ('') ve sıfır (0) birbirinden farklıdır. COUNT(*)tüm satırları sayar;COUNT(sutun)yalnızca o sütundaNULLolmayan değerleri sayar.ORDER BYolmadan sonuç sırası garanti edilmez.- Kullanıcı girdisini SQL metnine doğrudan eklemek yerine sürücünün parametre bağlama özelliğini kullan (SQL injection riskini önler).