Denetim sütunları
Neredeyse her iş tablosunda bulunur: created_at, created_by, updated_at, updated_by. Varsayılan değerle (DEFAULT CURRENT_TIMESTAMP) ve güncellemede trigger veya uygulama katmanıyla doldurulur. Ayrıntılı geçmiş gerekiyorsa temporal tablo veya ayrı bir geçmiş tablosu.
Soft delete (yumuşak silme)
Satırı silmek yerine is_deleted = 1 veya deleted_at işaretlemek. Geri alınabilirlik ve denetim sağlar ama bedeli vardır:
- Her sorguya
WHERE deleted_at IS NULLeklemek gerekir; unutulursa silinmiş veri raporlara karışır. Çözüm: aktif satırları gösteren bir görünüm. - Tekil kısıtlar bozulur (silinen e-postayla yeni kayıt açılamaz). Çözüm: filtreli/kısmi indeks (
CREATE UNIQUE INDEX ... WHERE deleted_at IS NULL, PostgreSQL ve SQL Server destekler). - KVKK açısından “silindi” sayılmaz; kişisel veri imhası ayrıca yapılmalı.
CREATE TABLE etiket ( etiket_id INTEGER PRIMARY KEY, ad TEXT NOT NULL, deleted_at TEXT ); INSERT INTO etiket VALUES (1, 'kampanya', NULL), (2, 'yeni', '2024-11-01'), (3, 'indirim', NULL); CREATE VIEW v_etiket AS SELECT etiket_id, ad FROM etiket WHERE deleted_at IS NULL; SELECT * FROM v_etiket;
EAV: varlık-öznitelik-değer tuzağı
“Her ürünün farklı özellikleri var” diye tek bir (urun_id, ozellik, deger) tablosu kurmak esnek görünür ama:
- Tip güvenliği yoktur (her değer metin), kısıt yazılamaz.
- Basit bir rapor bile çok sayıda self join veya pivot gerektirir.
- İndekslemek ve optimize etmek zordur.
Alternatifler: ortak alanlar sütun, değişken alanlar JSON sütunu (PostgreSQL jsonb + GIN indeks, SQL Server JSON fonksiyonları, MySQL JSON); ya da alt tip tabloları.
JSON mu, tablo mu?
| Tablo/sütun seç | JSON seç |
|---|---|
| Alan sık filtreleniyor, birleştiriliyor, toplanıyor | Alan yalnızca saklanıp aynen gösteriliyor |
| Kısıt ve tip güvenliği gerekiyor | Yapı kaynağa göre değişiyor (API yanıtları, olay yükleri) |
| Raporlama ve BI kullanacak | Seyrek, isteğe bağlı nitelikler |
Diğer kalıplar ve tuzaklar
- Virgülle ayrılmış liste (
etiketler = 'a,b,c'): 1NF ihlali; ara tablo kullan. - Çok amaçlı arama tablosu (tüm kodlar tek tabloda,
tursütunuyla): yabancı anahtar kurulamaz; her kod listesi ayrı tablo. - Polimorfik ilişki (
hedef_tur, hedef_id): yabancı anahtar yok; her hedef için ayrı nullable FK sütunu veya ayrı ara tablo. - Para birimi olmadan tutar: çok para birimli sistemde
currencysütunu şart. - Adlandırma: tutarlı tekil/çoğul, snake_case, anlamlı adlar (
tarih1değilorder_date); ayrılmış kelimelerden kaçın. - Durum sütunu: serbest metin yerine sınırlı değer listesi (CHECK veya arama tablosu).