PostgreSQL Nedir? SQL Sorguları ve Veritabanı Tasarımı Rehberi 2025
PostgreSQL nedir ve neden 2025'in en popüler açık kaynaklı veritabanı yönetim sistemi? Bu kapsamlı rehberde PostgreSQL'in temellerini, SQL sorgularını ve ilişkisel veritabanı tasarım prensiplerini öğreneceksiniz. MySQL ve MongoDB karşılaştırması, pratik SQL komutları (SELECT, INSERT, UPDATE, DELETE), JOIN işlemleri, normalizasyon teknikleri ve performans optimizasyonu ipuçlarını içeren bu kılavuz, hem yeni başlayanlar hem de deneyimli geliştiriciler için hazırlanmıştır. Instagram, Spotify ve Netflix gibi dev şirketlerin tercih ettiği PostgreSQL'i Supabase ile birlikte kullanarak modern web uygulamaları geliştirmeyi öğrenin. Blog sitesi ve e-ticaret örnekleriyle pratik yapın, ACID uyumluluğu ve Row Level Security (RLS) gibi güvenlik özelliklerini keşfedin.
Mehmet Karataş
Yazar

PostgreSQL'e Giriş: Veritabanının Kralı 🐘
Dostum, Veri Yönetiminin Temellerini Öğrenme Zamanı
Otur bi kahve al, sana PostgreSQL'den bahsedeceğim. Bu sadece bir veritabanı değil, neredeyse tüm modern web uygulamalarının kalbinde atan bir güç merkezi.
PostgreSQL Nedir?
Bak şimdi, her uygulama veriyi bir yerde saklamak zorunda. Kullanıcı bilgileri, blog yazıları, siparişler, yorumlar... Her şey bir yerde duruyor olmalı.
PostgreSQL (ya da kısaca Postgres) tam olarak bu işi yapan, açık kaynaklı, güçlü ve güvenilir bir ilişkisel veritabanı yönetim sistemi.
İlişkisel ne demek?
Verilerin tablolar halinde saklandığı ve bu tabloların birbirleriyle ilişkili olduğu bir sistem. Tıpkı Excel'deki tablolar gibi, ama çok daha güçlü ve organize.
Neden PostgreSQL?
Piyasada MySQL, MongoDB, SQLite gibi alternatifleri varken neden Postgres?
1. Güçlü ve Kararlı
30 yılı aşkın geliştirme geçmişi var. Bu kadar uzun süre ayakta kalan bir yazılım, işini iyi yapıyordur.
2. Açık Kaynak ve Ücretsiz
Lisans ücreti yok, topluluk desteği güçlü. İstediğin gibi kullan, istediğin gibi özelleştir.
3. ACID Uyumlu
Atomicity (Bütünlük)
Consistency (Tutarlılık)
Isolation (Yalıtım)
Durability (Kalıcılık)
Ne demek bu? Verinin güvenli, tutarlı ve kaybolmadan saklanacağı anlamına gelir.
4. JSON Desteği
Modern uygulamalarda JSON kullanımı çok yaygın. Postgres hem ilişkisel hem de JSON verisi saklayabiliyor. İkisinin en iyisi.
5. Ölçeklenebilir
Küçük bir blog sitesinden başla, milyonlarca kullanıcılı bir platforma dönüş. Postgres hepsini kaldırır.
6. Büyük Şirketlerin Tercihi
Instagram, Spotify, Netflix, Reddit... Hepsi Postgres kullanıyor. Sebepsiz değil.
PostgreSQL vs MySQL vs MongoDB
Hızlıca karşılaştıralım:
PostgreSQL:
İlişkisel veritabanı
ACID uyumlu
Karmaşık sorgular için mükemmel
JSON desteği var
Daha strict (kuralcı)
MySQL:
İlişkisel veritabanı
Basit, hızlı kurulum
Daha yaygın
Wordpress gibi platformlarda popüler
MongoDB:
NoSQL veritabanı
Esnek yapı
Şema gerekmez
Hızlı prototipleme için iyi
Karmaşık ilişkilerde zayıf
Benim tavsiyem: Eğer düzgün bir veritabanı yapısı kurmak istiyorsan, PostgreSQL öğren. Hem ilişkisel hem modern özellikleri birleştiriyor.
Temel Kavramlar
Veritabanı (Database)
Tüm verinin saklandığı ana konteyner. Bir proje = bir veritabanı genellikle.

Tablo (Table)
Verilerin satır ve sütunlar halinde tutulduğu yapı.

Sütun (Column)
Tablodaki her bir veri alanı. Örnek: id, username, email
Satır (Row)
Tablodaki her bir kayıt. Örnek: Ahmet'in tüm bilgileri bir satır.
Primary Key (Birincil Anahtar)
Her satırı benzersiz şekilde tanımlayan alan. Genellikle id kullanılır.
Foreign Key (Yabancı Anahtar)
Bir tablonun başka bir tabloyla ilişkisini sağlayan alan.
İlk Adımlar: Basit Bir Blog Veritabanı Tasarlayalım
Diyelim ki bir blog yapıyorsun. Neye ihtiyacın var?
1. Users Tablosu
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Açıklama:
SERIAL= otomatik artan sayı (1, 2, 3...)PRIMARY KEY= benzersiz tanımlayıcıVARCHAR(50)= maksimum 50 karakterUNIQUE= aynısından iki tane olamazNOT NULL= boş bırakılamazTIMESTAMP= tarih ve saat
2. Posts Tablosu
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
content TEXT NOT NULL,
author_id INTEGER REFERENCES users(id),
published BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Açıklama:
TEXT= sınırsız uzunlukta metinREFERENCES users(id)= users tablosuna bağlantı (Foreign Key)BOOLEAN= true/false
3. Comments Tablosu
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Açıklama:
ON DELETE CASCADE= post silinirse, yorumlar da silinir
Temel SQL Komutları
Veri Ekleme (INSERT)
-- Kullanıcı ekle
INSERT INTO users (username, email, password_hash)
VALUES ('mehmet', 'mehmet@email.com', 'hashed_password_123');
-- Blog yazısı ekle
INSERT INTO posts (title, slug, content, author_id, published)
VALUES (
'PostgreSQL Öğreniyorum',
'postgresql-ogreniyorum',
'Bu benim ilk blog yazım!',
1,
true
);
Veri Çekme (SELECT)
-- Tüm kullanıcıları getir
SELECT * FROM users;
-- Sadece username ve email getir
SELECT username, email FROM users;
-- Yayınlanmış yazıları getir
SELECT * FROM posts WHERE published = true;
-- En yeni 10 yazıyı getir
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10;
Veri Güncelleme (UPDATE)
-- Kullanıcının email'ini güncelle
UPDATE users
SET email = 'yeni@email.com'
WHERE id = 1;
-- Yazıyı yayınla
UPDATE posts
SET published = true, updated_at = CURRENT_TIMESTAMP
WHERE id = 1;
Veri Silme (DELETE)
-- Bir yorumu sil
DELETE FROM comments WHERE id = 1;
-- Yayınlanmamış yazıları sil
DELETE FROM posts WHERE published = false;
İlişkiler ve JOIN'ler
İşin sihirli kısmı burası. Tabloları birleştirip anlamlı bilgiler çıkartabilirsin.
INNER JOIN
-- Yazıları yazarlarıyla birlikte getir
SELECT
posts.title,
posts.content,
users.username as author
FROM posts
INNER JOIN users ON posts.author_id = users.id;
LEFT JOIN
-- Tüm kullanıcıları, yazı yazmamış olanları da getir
SELECT
users.username,
COUNT(posts.id) as post_count
FROM users
LEFT JOIN posts ON users.id = posts.author_id
GROUP BY users.username;
Karmaşık Sorgular
-- Her yazının yorum sayısını getir
SELECT
posts.title,
users.username as author,
COUNT(comments.id) as comment_count
FROM posts
INNER JOIN users ON posts.author_id = users.id
LEFT JOIN comments ON posts.id = comments.post_id
WHERE posts.published = true
GROUP BY posts.id, posts.title, users.username
ORDER BY comment_count DESC;
Veritabanı Tasarım İlkeleri
1. Normalizasyon
Veriyi tekrar etmemek için yapılan düzenleme.
Kötü Tasarım ❌

Sorun: Ahmet'in emaili her satırda tekrar ediyor. Email değişirse tüm satırları güncellemen gerek.
İyi Tasarım ✅

Çözüm: Email sadece users tablosunda bir kere. Değişirse tek yerden güncellersin.
2. İndeksleme
Sorguları hızlandırmak için kullanılır.
-- Email'e göre sık arama yapıyorsan
CREATE INDEX idx_users_email ON users(email);
-- Slug'a göre sık arama yapıyorsan
CREATE INDEX idx_posts_slug ON posts(slug);
Indeks yoksa veritabanı tüm satırları tarar. Indeks varsa direkt bulur.
3. Veri Tipleri Seçimi
Doğru veri tipini kullan:
Sayılar: INTEGER, BIGINT, DECIMAL
Metinler: VARCHAR, TEXT
Tarihler: DATE, TIMESTAMP
Boolean: BOOLEAN
JSON: JSON, JSONB
4. Constraint'ler (Kısıtlamalar)
Veri bütünlüğünü korumak için:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) CHECK (price > 0),
stock INTEGER CHECK (stock >= 0),
category_id INTEGER REFERENCES categories(id)
);
Supabase ile PostgreSQL
Supabase kullanıyorsan, zaten PostgreSQL kullanıyorsun demektir. Supabase, PostgreSQL'in üzerine kurulmuş bir platform.
Supabase'de Tablo Oluşturma
Dashboard'dan:
Table Editor'e git
"New Table" butonuna tıkla
Sütunları ekle
Save
SQL Editor'den:
-- Supabase SQL Editor'de çalıştır
CREATE TABLE todos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id),
task TEXT NOT NULL,
completed BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT NOW()
);
Row Level Security (RLS)
Supabase'in en güçlü özelliği. Veritabanı seviyesinde güvenlik:
-- Kullanıcılar sadece kendi todo'larını görsün
CREATE POLICY "Users can view own todos"
ON todos FOR SELECT
USING (auth.uid() = user_id);
-- Kullanıcılar sadece kendi todo'larını ekleyebilsin
CREATE POLICY "Users can insert own todos"
ON todos FOR INSERT
WITH CHECK (auth.uid() = user_id);
Pratik Örnekler
Örnek 1: Blog Sitesi
İhtiyaçlar:
Kullanıcılar
Blog yazıları
Kategoriler
Yorumlar
Etiketler
Tablo yapısı:
-- Kullanıcılar
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
bio TEXT,
avatar_url TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Kategoriler
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL
);
-- Yazılar
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
content TEXT NOT NULL,
excerpt TEXT,
cover_image TEXT,
author_id INTEGER REFERENCES users(id),
category_id INTEGER REFERENCES categories(id),
published BOOLEAN DEFAULT false,
view_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Etiketler
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL
);
-- Yazı-Etiket ilişkisi (Many-to-Many)
CREATE TABLE post_tags (
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag_id)
);
-- Yorumlar
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
parent_id INTEGER REFERENCES comments(id),
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
Örnek 2: E-Ticaret Sitesi
İhtiyaçlar:
Ürünler
Kategoriler
Sipariş sistemi
Sepet
İncelemeler
-- Ürünler
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
stock INTEGER NOT NULL DEFAULT 0 CHECK (stock >= 0),
category_id INTEGER REFERENCES categories(id),
image_url TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Siparişler
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
shipping_address TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Sipariş detayları
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
price DECIMAL(10, 2) NOT NULL,
subtotal DECIMAL(10, 2) NOT NULL
);
-- İncelemeler
CREATE TABLE reviews (
id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES products(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
rating INTEGER CHECK (rating >= 1 AND rating <= 5),
comment TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
Performans İpuçları
1. EXPLAIN Kullan
Sorgunun nasıl çalıştığını görmek için:
EXPLAIN ANALYZE
SELECT * FROM posts WHERE author_id = 1;
2. İndeksle
Sık kullanılan sütunları indeksle:
CREATE INDEX idx_posts_author ON posts(author_id);
CREATE INDEX idx_posts_slug ON posts(slug);
CREATE INDEX idx_posts_published ON posts(published);
3. LIMIT Kullan
Gereksiz veri çekme:
-- Kötü
SELECT * FROM posts;
-- İyi
SELECT * FROM posts LIMIT 20;
4. SELECT * Kullanma
Sadece ihtiyacın olanı çek:
-- Kötü
SELECT * FROM users;
-- İyi
SELECT id, username, email FROM users;
Yaygın Hatalar ve Çözümleri
1. N+1 Problem
-- Kötü: Her yazı için ayrı sorgu
SELECT * FROM posts;
-- Sonra her post için:
SELECT * FROM users WHERE id = post.author_id;
-- İyi: Tek sorguda al
SELECT
posts.*,
users.username
FROM posts
JOIN users ON posts.author_id = users.id;
2. Cascade Silme Unutmak
-- İlişkili verileri otomatik sil
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE
3. İndeks Eksikliği
WHERE, JOIN ve ORDER BY'da kullanılan sütunları indeksle.
4. Veri Tipi Hatası
-- Yanlış
age VARCHAR(10) -- Yaş string mi?
-- Doğru
age INTEGER
Sonuç: PostgreSQL Neden Önemli?
Dostum, her modern uygulama veriyi bir yerde saklamak zorunda. PostgreSQL bu işin standardı haline gelmiş durumda.
Neden öğrenmelisin?
Temel Beceri: Full-stack developer olmak istiyorsan şart
Güçlü: Küçük projeden dev uygulamaya kadar her şeyi kaldırır
Popüler: İş ilanlarının çoğunda aranan beceri
Ücretsiz: Açık kaynak, maliyet yok
Modern: Hem SQL hem JSON, ikisinin gücü
Nereden başla?
Supabase üzerinden pratik yap (ücretsiz)
Basit bir TODO app yap
Blog projesi yap
E-ticaret sitesi tasarla
Her projede veri modelini düşün
PostgreSQL öğrenmek biraz zaman alır ama değer. Bir kere öğrendin mi, tüm veritabanı dünyası sana açılır.
Başla, pratik yap, hata yap, öğren. Veri yönetimi gücü senin elinde olsun.
Kod yazmak için varız!
Yorumlar (0)
Henüz yorum yapılmamış. İlk yorumu sen yap!
Yorum Yap