Sql & Veritabanı İle İlişkisel Veri Modeli Tasarımı Nasıl Yapılır?

Sql & Veritabanı İle İlişkisel Veri Modeli Tasarımı Nasıl Yapılır?
Sql & Veritabanı İle İlişkisel Veri Modeli Tasarımı Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

İlişkisel veri modelleme çalışmalarına başlamadan önce, sisteminizde güncel bir SQL motoru bulunmalıdır. 2026 yılı itibarıyla PostgreSQL 17 veya MySQL 9.x sürümleri, endüstri standartları olarak önerilmektedir. Ayrıca, veritabanı şemalarını görselleştirmek için DBeaver veya pgAdmin gibi bir arayüz aracı kullanmanız, tasarladığınız yapıyı test etmenizi kolaylaştıracaktır.

  • Veritabanı Yönetim Sistemi: PostgreSQL 17 (Önerilen)
  • Görselleştirme Aracı: DBeaver veya MySQL Workbench
  • Temel Bilgi: SQL sözdizimine aşinalık

Adım 1: Gereksinim Analizi ve Varlık-İlişki Diyagramı (ERD)

Veritabanı tasarımına başlamadan önce, uygulamanızdaki "varlıkları" (entities) belirlemelisiniz. Örneğin, bir e-ticaret sistemi tasarlıyorsanız; Kullanıcılar, Ürünler ve Siparişler temel varlıklarınızdır. Bu varlıkların birbirleriyle olan ilişkilerini (bire-bir, bire-çok, çok-ağa-çok) kağıt üzerinde veya bir araç yardımıyla çizmek, kod yazmadan önce hataları görmenizi sağlar.

İlişkileri belirlerken şu soruyu sorun: "Bir kullanıcı birden fazla sipariş verebilir mi?" Cevabınız evet ise, bu bire-çok (one-to-many) bir ilişkidir. Tasarım aşamasında bu mantığı SQL koduna dökmek için yabancı anahtarları (foreign keys) kullanacağız.

Adım 2: Tabloları Oluşturma ve Birincil Anahtarlar

SQL'de tabloları oluştururken her satırın benzersiz olduğunu garanti etmek için birincil anahtarlar (Primary Key) tanımlanmalıdır. 2026 standartlarında, performans ve güvenlik gerekçesiyle genellikle UUID veya BIGSERIAL veri tipleri tercih edilir.

CREATE TABLE kullanicilar (
    kullanici_id BIGSERIAL PRIMARY KEY,
    ad VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    olusturulma_tarihi TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Yukarıdaki kod, kullanicilar tablosunu oluşturur. BIGSERIAL, veritabanının otomatik olarak benzersiz bir ID atamasını sağlar. UNIQUE kısıtlaması ise aynı e-posta adresiyle birden fazla hesap açılmasını engeller.

Adım 3: İlişkisel Bağlantılar ve Foreign Key Kullanımı

Tablolar arası ilişki kurmak, veritabanının ilişkisel (relational) doğasını ortaya çıkarır. Bir sipariş tablosu oluştururken, hangi kullanıcının sipariş verdiğini belirtmek için kullanici_id sütununu FOREIGN KEY olarak tanımlarız.

CREATE TABLE siparisler (
    siparis_id BIGSERIAL PRIMARY KEY,
    kullanici_id BIGINT REFERENCES kullanicilar(kullanici_id) ON DELETE CASCADE,
    toplam_tutar DECIMAL(10, 2) NOT NULL,
    siparis_tarihi TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Burada ON DELETE CASCADE komutu, bir kullanıcı sistemden silindiğinde, o kullanıcıya ait tüm siparişlerin de otomatik olarak silinmesini sağlar. Bu, veritabanı bütünlüğünü korumak için kritik bir adımdır.

Adım 4: Veri Normalizasyonu (1NF, 2NF, 3NF)

Veri normalizasyonu, tablolardaki veri tekrarını azaltmak ve tutarsızlıkları gidermek için kullanılan bir süreçtir. 1. Normal Form (1NF), her hücrede tek bir değer olması gerektiğini söyler. 2. Normal Form, kısmi bağımlılıkları ortadan kaldırır. 3. Normal Form ise, anahtar olmayan sütunların birbirine bağımlı olmamasını zorunlu kılar.

Normal Form Temel Amaç Avantajı
1NF Atomik veri yapısı Veri tutarlılığı
2NF Kısmi bağımlılığı kaldırma Veri tekrarını azaltma
3NF Geçişli bağımlılığı kaldırma Anomali önleme

Adım 5: İndeksleme ile Performans Optimizasyonu

Veritabanı büyüdükçe, sorgu hızları düşebilir. Sıkça arama yaptığınız sütunlara indeks eklemek, veritabanının veriyi "kitabın içindekiler kısmı" gibi hızlıca bulmasını sağlar. Ancak gereğinden fazla indeks, yazma (INSERT/UPDATE) işlemlerini yavaşlatabilir.

CREATE INDEX idx_kullanici_email ON kullanicilar(email);
CREATE INDEX idx_siparis_tarihi ON siparisler(siparis_tarihi);

Bu kod, özellikle e-posta ile giriş yaparken veya belirli tarih aralığındaki siparişleri listelerken sorgu performansını ciddi oranda artırır.

Adım 6: Güvenli Sorgulama ve SQL Injection Koruması

Veritabanı tasarımınız ne kadar mükemmel olursa olsun, uygulamanız SQL Injection saldırılarına açıksa verileriniz güvende değildir. Kullanıcıdan gelen verileri asla doğrudan sorguya eklemeyin; her zaman parametreli sorgular (prepared statements) kullanın.

-- Yanlış Kullanım (SQL Injection riski)
-- "SELECT * FROM kullanicilar WHERE email = '" + kullaniciGirdisi + "';"

-- Doğru Kullanım (Parametreli Sorgu)
PREPARE get_user (text) AS
SELECT * FROM kullanicilar WHERE email = $1;
EXECUTE get_user('ornek@email.com');
Kritik Güvenlik Uyarısı: Veritabanı bağlantı bilgilerini (şifreler, host adresleri) hiçbir zaman kaynak kodun içine gömmeyin. Mutlaka .env dosyaları veya güvenli kasa (Vault) çözümleri kullanın. Üretim ortamında veritabanı kullanıcısının yetkilerini, sadece ihtiyaç duyduğu tablolarla sınırlayın (Principle of Least Privilege).

Sıkça Sorulan Sorular

İlişkisel veritabanı mı, NoSQL mi seçmeliyim?

Eğer verileriniz arasında güçlü ilişkiler varsa ve veri tutarlılığı (ACID prensipleri) önceliğinizse, ilişkisel veritabanı (SQL) tercih etmelisiniz. NoSQL, daha çok esnek şema yapısına ihtiyaç duyan ve yatayda ölçeklenmesi gereken büyük veri projeleri için uygundur.

Yabancı anahtar (Foreign Key) performans düşürür mü?

Doğru indekslenmiş bir yabancı anahtar, performans düşürmez; aksine veritabanı bütünlüğünü koruyarak hatalı veri girişini engeller. Performans sorunu genellikle eksik indekslerden kaynaklanır.

UUID mi yoksa Serial ID mi kullanmalıyım?

Serial ID'ler (artan sayılar) daha az yer kaplar ve okunması kolaydır. Ancak dağıtık sistemlerde veya verinin gizli kalması gereken durumlarda (ID'nin tahmin edilmemesi için) UUID kullanmak daha güvenli bir tercihtir.

Normalizasyon her zaman gerekli midir?

Normalizasyon, veri bütünlüğü için şarttır. Ancak çok yoğun okuma yapılan (Read-heavy) raporlama sistemlerinde, performansı artırmak için "denormalizasyon" (tabloları birleştirme) yöntemine başvurulabilir. Bu, profesyonel bir denge meselesidir.

Veritabanı tasarımını nasıl güncellerim?

Veritabanı şemasını değiştirmek için "Migration" (Göç) araçlarını kullanmalısınız. SQL dosyalarını manuel çalıştırmak yerine, projenizde kullandığınız dilin (örneğin Laravel için Migrations, Node.js için Sequelize/Prisma) sunduğu araçlarla şema değişikliklerini versiyon kontrol sistemine (Git) dahil etmelisiniz.

İleri Düzey İpucu: Veritabanı Performansını İzleme ve EXPLAIN ANALYZE Kullanımı

Veritabanı tasarımınız ne kadar kusursuz olursa olsun, karmaşık sorgular zamanla sistemin darboğazı haline gelebilir. SQL sorgularınızın arka planda nasıl çalıştığını anlamak için EXPLAIN ANALYZE komutu en güçlü aracınızdır. Bu komut, veritabanı motorunun sorguyu yürütmek için hangi indeksleri kullandığını, hangi tabloları taradığını ve işlemin ne kadar sürdüğünü detaylı bir şekilde raporlar.

Aşağıdaki örnekte, bir kullanıcılar tablosunda e-posta adresi üzerinden yapılan aramanın performansını nasıl analiz edeceğinizi görebilirsiniz:

-- Sorgunun çalışma planını ve süresini analiz et
EXPLAIN ANALYZE 
SELECT id, username, email 
FROM users 
WHERE email = 'ornek@email.com';

Çıktıda Seq Scan (Sıralı Tarama) ifadesini görüyorsanız, bu durum veritabanının tüm tabloyu baştan sona taradığı anlamına gelir. Eğer tablo boyutunuz büyükse, bu performans kaybına yol açar. Bu durumda ilgili sütuna bir INDEX ekleyerek sorgu planını Index Scan seviyesine çekmeniz gerekir.

Gerçek Senaryo: Çoktan Çoka (Many-to-Many) İlişkilerde Ara Tablo Yönetimi

İlişkisel modellemede en sık karşılaşılan zorluklardan biri, "Öğrenciler" ve "Dersler" gibi birbirine çoktan çoka bağlı olan varlıkların yönetimidir. Bu tür yapılarda verinin tutarlılığını korumak için bir "Ara Tablo" (Junction Table) oluşturulması zorunludur. Ara tablo, her iki ana tablonun birincil anahtarlarını yabancı anahtar olarak tutar.

Aşağıdaki örnek, bir öğrencinin birden fazla derse, bir dersin ise birden fazla öğrenciye sahip olduğu bir yapıyı göstermektedir:

-- Ara tablo oluşturma
CREATE TABLE student_courses (
    student_id INT NOT NULL,
    course_id INT NOT NULL,
    enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
    FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE
);

-- İlişkili verileri çekme
SELECT s.name, c.title 
FROM students s
JOIN student_courses sc ON s.id = sc.student_id
JOIN courses c ON sc.course_id = c.id;

Önemli İpucu: Ara tablolarda ON DELETE CASCADE kısıtlamasını kullanmak, ana kayıt silindiğinde (örneğin bir öğrenci sistemden silindiğinde) ona bağlı olan tüm kayıtların otomatik olarak temizlenmesini sağlar. Bu, veritabanınızda "çöp veri" (orphan records) birikmesini engelleyerek veri bütünlüğünü korur.

Profesyonel Tavsiye: Büyük ölçekli sistemlerde, veritabanı şemanızdaki değişiklikleri yaparken mutlaka bir "staging" (test) ortamında EXPLAIN çıktılarını kontrol edin. Üretim ortamına almadan önce sorgularınızın indeks kullanıp kullanmadığını doğrulamak, sistemin çökmesini engelleyen en kritik güvenlik önlemidir.

Sonuç

Sql & Veritabanı ile ilişkisel veri modeli tasarımı, bir uygulamanın omurgasını oluşturur. Bu rehberde öğrendiğiniz adımları takip ederek, verilerinizi tutarlı, hızlı ve güvenli bir şekilde saklayabilirsiniz. Tasarım sürecinde her zaman "bütünlük" ve "ölçeklenebilirlik" kavramlarını ön planda tutun. Bir sonraki adım olarak, veritabanı sorgularınızı optimize etmek için EXPLAIN ANALYZE komutunu öğrenerek sorgu planlarını incelemeye başlayabilirsiniz.

Bu yazıya tepkinizi paylaşın:
Mert Çelik

Hobi ve teknik beceriler üzerine eğitici içerikler kurguluyorum. Okuyucuların günlük hayatta karşılaştığı sorunlara uygulanabilir çözümler sunmayı hedefliyorum.

Yorumlar (0)

Yorum Yaz