- PostgreSQL ve MySQL'de doğru tipleri seçmek (özellikle para, tarih ve JSON için) ve otomatik anahtarlar oluşturmak
- SQLite ile sunucu VTYS'leri arasındaki temel farkları açıklamak
- En az yetki ilkesine göre roller ve izinler kurmak, yedekleme ve geri yükleme planlamak
Bu kurstaki tüm örnekler SQLite'ta çalışır: tek bir dosyada yaşayan ve telefonlarda, tarayıcılarda, küçük programlarda vazgeçilmez olan bir kütüphane. Bir web sitesinin ya da mobil uygulamanın arkasında ise genellikle bir sunucu VTYS'si durur; çoğunlukla PostgreSQL ya da MySQL. Bunlar ağ üzerinden binlerce bağlantıya hizmet eder; katı tipler, kullanıcılar ve izinler, yedekleme araçları sunar. SQL bilginin yaklaşık %90'ı aynı kalır, ama geri kalan %10 canlı ortamda ciddi hataların kaynağıdır. Bu ders o farklara ayrılmıştır.
SQLite'tan temel farklar
| Özellik | SQLite | PostgreSQL | MySQL (InnoDB) |
|---|---|---|---|
| Mimari | uygulamanın içindeki kütüphane, tek dosya | istemci–sunucu | istemci–sunucu |
| Tipler | esnek (STRICT tablolar isteğe bağlı) | katı | katı (sql_mode ayarına bağlı) |
| Eş zamanlı yazma | aynı anda tek yazıcı | MVCC, çok sayıda yazıcı | MVCC, çok sayıda yazıcı |
| Kullanıcılar | yok — dosya izinleri | roller, GRANT | 'user'@'host', GRANT |
| Otomatik id | INTEGER PRIMARY KEY | GENERATED … AS IDENTITY | AUTO_INCREMENT |
| Mantıksal tip | 0 / 1 tam sayı | BOOLEAN | TINYINT(1) |
Hangisini seçmeli? İkisi de ücretsiz ve açık kaynaklıdır, ikisi de yıllardır büyük projelerde sınanmıştır. PostgreSQL zengin tipleri (JSONB, diziler, aralıklar), güçlü eklentileri (örneğin coğrafi veriler için PostGIS) ve SQL standardına yakınlığıyla öne çıkar; bu yüzden analitik ve karmaşık iş mantığı için sık seçilir. MySQL web barındırma hizmetlerinde çok yaygındır; kurulumu ve çoğaltması (replikasyon) basittir. Uygulamada seçim çoğu zaman ekibin deneyimine ve altyapıya bağlıdır; bu dersteki bilgiler ise ikisi için de gereklidir.
-- SQLite: an ordinary table accepts almost anything
CREATE TABLE loose (n INTEGER);
INSERT INTO loose VALUES ('abc');
-- a STRICT table (SQLite 3.37+) behaves like PostgreSQL
CREATE TABLE strict_t (n INTEGER) STRICT;
INSERT INTO strict_t VALUES ('abc');▸ Beklenen çıktı
Error: cannot store TEXT value in INTEGER column strict_t.n
'abc' metnini tam sayı sütununa sessizce yazar; PostgreSQL ve STRICT tablo ise hata verir.Veri tipleri ve otomatik anahtarlar
Bir sunucu VTYS'sinde tip hem depolamayı hem de doğruluğu etkiler. Para için her zaman kesin bir ondalık tip kullan: PostgreSQL'de NUMERIC(10, 2), MySQL'de DECIMAL(10, 2). Zaman için PostgreSQL'de saat dilimini dikkate alan TIMESTAMPTZ tipini seç. Otomatik anahtarı PostgreSQL 10'dan beri kullanılabilen standart yazımla oluştur: GENERATED ALWAYS AS IDENTITY; eski SERIAL hâlâ çalışır ama yeni projelerde önerilmez. MySQL'de AUTO_INCREMENT kullanılır.
-- PostgreSQL
CREATE TABLE products (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category VARCHAR(50),
price NUMERIC(10, 2) NOT NULL CHECK (price >= 0),
stock INTEGER NOT NULL DEFAULT 0,
attributes JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO products (name, category, price, attributes)
VALUES ('Laptop', 'Electronics', 1450.00, '{"ram_gb": 16, "color": "silver"}')
RETURNING id, created_at;id | created_at ----+------------------------------- 1 | 2025-09-01 10:15:42.318524+04 (1 row) INSERT 0 1
psql), çalıştırılamaz. RETURNING yeni satırın değerlerini ek bir sorgu olmadan döndürür; +04, Bakü saatidir.-- MySQL 8
CREATE TABLE products (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category VARCHAR(50),
price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
stock INT NOT NULL DEFAULT 0,
attributes JSON,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;Query OK, 0 rows affected (0.03 sec)
utf8, eski 3 baytlık kodlamadır (utf8mb3) ve emojiler gibi bazı karakterleri saklayamaz; her zaman utf8mb4 yaz (MySQL 8.0'da varsayılan budur).SELECT 0.1 + 0.2 AS a,
1450 * 0.07 AS b,
899.99 * 3 AS c;▸ Beklenen çıktı
a | b | c 0.30000000000000004 | 101.50000000000001 | 2699.9700000000003
Alıştırma veritabanındaki orders(id INTEGER PRIMARY KEY, customer_id INTEGER, product_id INTEGER, quantity INTEGER, order_date TEXT) tablosunu PostgreSQL için yeniden yaz. Hangi tipleri ve kısıtlamaları seçerdin?
Çözümü gösterÇözümü gizle
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY — milyarlarca sipariş için yer, standart yazım.2)
customer_id BIGINT NOT NULL REFERENCES customers(id) ve product_id BIGINT NOT NULL REFERENCES products(id) — PostgreSQL yabancı anahtarları her zaman denetler; SQLite'taki gibi PRAGMA gerekmez.3)
quantity INTEGER NOT NULL CHECK (quantity > 0) — eksi ya da sıfır miktar iş kuralına aykırıdır.4)
order_date DATE NOT NULL DEFAULT CURRENT_DATE — metin değil, gerçek bir tarih: geçersiz bir tarih ('2025-02-30') girilemez ve tarih fonksiyonları doğrudan çalışır.5) Yabancı anahtarlara indeks:
CREATE INDEX ON orders (customer_id) — PostgreSQL bunları otomatik oluşturmaz.JSON sütunları
Ürün özellikleri kategoriden kategoriye değişir: dizüstü bilgisayarın belleği, monitörün ekran boyutu vardır. Bu tür değişken nitelikleri bir JSON sütununda saklamak pratiktir. PostgreSQL'de JSONB ikili biçimde saklanır; anahtarlara -> (JSON döndürür) ve ->> (metin döndürür) ile erişilir, @> ise bir GIN indeksiyle hızlanan “içerir” denetimidir. MySQL'de JSON tipi ve ->>'$.anahtar' yazımı vardır. SQLite'ta da JSON fonksiyonları bulunur; aşağıdaki örneği çalıştır.
-- PostgreSQL
CREATE INDEX idx_products_attr ON products USING GIN (attributes);
SELECT name,
attributes ->> 'color' AS color,
(attributes ->> 'ram_gb')::int AS ram
FROM products
WHERE attributes @> '{"color": "silver"}';name | color | ram --------+--------+----- Laptop | silver | 16 (1 row)
::int, PostgreSQL'in tip dönüştürme yazımıdır (CAST(... AS INT) ile aynı).-- SQLite (3.38+): JSON stored as text
CREATE TABLE gadgets (id INTEGER PRIMARY KEY, name TEXT, attributes TEXT);
INSERT INTO gadgets (name, attributes) VALUES
('Laptop', '{"ram_gb": 16, "color": "silver"}'),
('Smartphone', '{"ram_gb": 8, "color": "black"}'),
('Tablet', '{"ram_gb": 8, "color": "silver"}'),
('Monitor', '{"size_in": 27}');
SELECT name,
attributes ->> '$.color' AS color,
attributes ->> '$.ram_gb' AS ram_gb
FROM gadgets
ORDER BY id;▸ Beklenen çıktı
name | color | ram_gb Laptop | silver | 16 Smartphone | black | 8 Tablet | silver | 8 Monitor | NULL | NULL
Kullanıcılar, roller ve yetkiler
Bir sunucu VTYS'si her bağlantının kime ait olduğunu bilir ve her işlemi izinlerle denetler. Temel kural en az yetki ilkesidir: her kullanıcı yalnızca işi için gereken hakları alır. Bir uygulama asla süper kullanıcı (postgres, root) olarak bağlanmamalıdır: SQL enjeksiyonu gibi bir zayıflık varsa saldırgan tüm veritabanını silebilir.
school veritabanı için iki rol gerekiyor: web uygulaması verileri okuyup değiştirebilmeli ama tablo oluşturup silememeli; analist ise yalnızca okuyabilmeli. Bu, PostgreSQL'de nasıl kurulur?
Çözümü gösterÇözümü gizle
CREATE ROLE ... LOGIN ve GRANT CONNECT ON DATABASE school.2) Şemayı kullanma hakkı:
GRANT USAGE ON SCHEMA public; CREATE hakkı verilmez, bu yüzden tablo oluşturulamaz.3) Uygulamaya:
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public; analiste yalnızca GRANT SELECT.4)
ALL TABLES yalnızca var olan tabloları kapsar; gelecekteki tablolar için ALTER DEFAULT PRIVILEGES yazılır.5) Tabloların sahibi ayrı bir “geçiş” (migration) rolüdür ve yapı değişiklikleri yalnızca onunla yapılır.
-- PostgreSQL
CREATE ROLE school_app LOGIN PASSWORD 'change-me';
GRANT CONNECT ON DATABASE school TO school_app;
GRANT USAGE ON SCHEMA public TO school_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO school_app;
CREATE ROLE analyst LOGIN PASSWORD 'change-me-too';
GRANT CONNECT ON DATABASE school TO analyst;
GRANT USAGE ON SCHEMA public TO analyst;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst;
-- MySQL: CREATE USER 'school_app'@'10.0.0.%' IDENTIFIED BY '...';
-- GRANT SELECT, INSERT, UPDATE, DELETE ON school.* TO 'school_app'@'10.0.0.%';CREATE ROLE GRANT GRANT GRANT CREATE ROLE GRANT GRANT GRANT
'ad'@'host' çiftiyle tanımlanır.Yedekler ve geri yükleme
Mantıksal bir yedek, veritabanını SQL komutlarına ya da özel bir arşive dönüştürür: PostgreSQL'de pg_dump, MySQL'de mysqldump. Sürümler arasında taşıma için kullanışlıdır, ama yalnızca alındığı anı saklar. Fiziksel bir yedek (pg_basebackup) ile günlük dosyalarının arşivlenmesi (WAL archiving) ise veritabanını herhangi bir ana geri döndürmeyi (point-in-time recovery) sağlar. SQLite'ta bunun için VACUUM INTO 'dosya.db' ya da kabuktaki .backup komutu vardır.
# PostgreSQL: custom-format dump and a test restore into another database
pg_dump -U postgres -d school -F c -f school_2025-09-01.dump
createdb -U postgres school_test
pg_restore -U postgres -d school_test school_2025-09-01.dump
# MySQL: a consistent InnoDB dump without locking tables
mysqldump -u root -p --single-transaction school > school_2025-09-01.sql
ls -lh school_2025-09-01.*-rw-r--r-- 1 admin admin 2.4M Sep 1 03:00 school_2025-09-01.dump -rw-r--r-- 1 admin admin 7.9M Sep 1 03:02 school_2025-09-01.sql
pg_dump ve pg_restore hiçbir şey yazdırmaz; dosya boyutları örnektir.Başlangıç kodundaki gadgets tablosundan rengi silver olan cihazların adını ve belleğini (ram_gb) JSON işleci ->> ile seç. Sonucu ada göre sırala.
CREATE TABLE gadgets (id INTEGER PRIMARY KEY, name TEXT, attributes TEXT);
INSERT INTO gadgets (name, attributes) VALUES
('Laptop', '{"ram_gb": 16, "color": "silver"}'),
('Smartphone', '{"ram_gb": 8, "color": "black"}'),
('Tablet', '{"ram_gb": 8, "color": "silver"}'),
('Monitor', '{"size_in": 27}');
-- your SELECT here▸ Beklenen çıktı
name | ram_gb Laptop | 16 Tablet | 8
Depodaki malların toplam değerini kayan nokta hatası olmadan hesapla: her fiyatı manatın yüzde birine çevir (CAST(ROUND(price * 100) AS INTEGER)), stokla çarp ve topla (total_qapik), sonra manat olarak göster (total_manat).
SELECT SUM(price * stock) AS float_total -- replace with exact integer math
FROM products;▸ Beklenen çıktı
total_qapik | total_manat 4026060 | 40260.6
Önemli noktalar
- SQLite tek bir dosyada yaşayan bir kütüphanedir; PostgreSQL ve MySQL ise katı tipli, çok sayıda yazıcıyı destekleyen istemci–sunucu sistemleridir.
- Para için
NUMERIC/DECIMAL, otomatik anahtar içinGENERATED ... AS IDENTITY(PostgreSQL) veAUTO_INCREMENT(MySQL). JSONBveJSONdeğişken nitelikler içindir; aranan ya da kısıtlama gerektiren alanlar sıradan sütun olmalıdır.- En az yetki: uygulama, analistler ve geçişler için ayrı roller; uygulama asla süper kullanıcı olarak bağlanmaz.
- Mantıksal (
pg_dump,mysqldump) ve fiziksel yedekler ile WAL arşivi; 3-2-1 kuralı ve düzenli geri yükleme testi.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.