İçeriğe geç
pitonmert

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ı

DML: INSERT · UPDATE · DELETE

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 NULL kullanı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ütunda NULL olmayan değerleri sayar.
  • ORDER BY olmadan 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).

Kaynaklar