MariaDB ve MySQL’de DEFAULT Değer Atama ve Kullanımı

Veritabanı tasarımı yaparken en çok göz ardı edilen konulardan biri DEFAULT değer atamasıdır. Bir tabloya veri eklerken her alanı doldurmak zorunda kalmak hem geliştirici hem de DBA açısından ciddi bir yük oluşturur. MariaDB ve MySQL’de DEFAULT mekanizmasını doğru kullanmak, hem veri bütünlüğünü korur hem de uygulama kodunu önemli ölçüde sadeleştirir. Bu yazıda DEFAULT değer atamasını tüm boyutlarıyla ele alacağız.

DEFAULT Nedir ve Neden Önemlidir?

DEFAULT, bir sütuna veri girilmediğinde otomatik olarak atanacak değeri tanımlar. Kulağa basit geliyor ama işin içine girince hem performans hem de bakım açısından ne kadar kritik olduğunu anlıyorsunuz.

Örneğin bir e-ticaret sitesinde sipariş durumu sütununuz var. Her yeni sipariş oluşturulduğunda durumun “pending” olması gerekiyor. Bunu uygulama katmanında yönetmek yerine veritabanı seviyesinde tanımlamak çok daha güvenli. Uygulama kodu hata verse bile veritabanı tutarlı kalır.

DEFAULT kullanmanın sağladığı faydalar şunlardır:

  • Veri tutarlılığı: Uygulama katmanından bağımsız olarak veritabanı seviyesinde tutarlılık sağlanır
  • Geliştirme kolaylığı: INSERT sorgularını kısaltır, gereksiz parametre geçişini önler
  • Bakım kolaylığı: İş kuralı değiştiğinde tek bir ALTER TABLE yeterli olur
  • NULL karmaşasını azaltır: Anlamlı varsayılan değerlerle NULL ile başa çıkma yükü azalır

Temel DEFAULT Kullanımı

Önce en basit halinden başlayalım. Tablo oluştururken DEFAULT tanımlamak:

mysql -u root -p

CREATE TABLE kullanicilar (
    id INT AUTO_INCREMENT PRIMARY KEY,
    kullanici_adi VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    aktif TINYINT(1) DEFAULT 1,
    rol VARCHAR(20) DEFAULT 'user',
    kayit_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
    deneme_hakki INT DEFAULT 3,
    profil_resmi VARCHAR(255) DEFAULT 'avatar_default.png'
);

Bu tabloya şöyle bir INSERT atalım:

INSERT INTO kullanicilar (kullanici_adi, email)
VALUES ('ahmet_yilmaz', '[email protected]');

SELECT * FROM kullanicilar WHERE kullanici_adi = 'ahmet_yilmaz';

Çıktıda aktif sütununun 1, rol sütununun ‘user’, deneme_hakki sütununun 3 olduğunu göreceksiniz. Hiçbirini INSERT sorgusunda belirtmemize gerek kalmadı.

DEFAULT Anahtar Kelimesini Açıkça Kullanmak

Bazen INSERT sorgusunda bir sütunu listeye dahil etmek ama yine de varsayılan değerini kullanmak isteyebilirsiniz. Bunun için DEFAULT anahtar kelimesini doğrudan kullanabilirsiniz:

INSERT INTO kullanicilar (kullanici_adi, email, aktif, rol)
VALUES ('mehmet_kaya', '[email protected]', DEFAULT, DEFAULT);

-- Yukaridaki sorgu ile asagidaki ayni sonucu verir
INSERT INTO kullanicilar (kullanici_adi, email)
VALUES ('mehmet_kaya', '[email protected]');

Bu özellikle dinamik SQL üretirken işe yarar. Sütun listesini sabit tutup değerleri duruma göre DEFAULT veya gerçek değer olarak belirtebilirsiniz.

Tarih ve Zaman Alanlarında DEFAULT

Tarih/zaman alanları DEFAULT’un en çok kullanıldığı yerlerdir. Kayıt oluşturma ve güncelleme zamanlarını otomatik yönetmek klasik bir ihtiyaçtır:

CREATE TABLE siparisler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    musteri_id INT NOT NULL,
    tutar DECIMAL(10,2) NOT NULL,
    durum VARCHAR(20) DEFAULT 'pending',
    olusturma_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
    guncelleme_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    tamamlanma_tarihi DATETIME DEFAULT NULL
);

Burada dikkat edilmesi gereken nokta ON UPDATE CURRENT_TIMESTAMP ifadesidir. Bu, kayıt her güncellendiğinde guncelleme_tarihi sütununu otomatik olarak güncelleyen bir mekanizmadır. tamamlanma_tarihi ise başlangıçta NULL olarak kalır ve sipariş tamamlandığında manuel olarak güncellenir.

MariaDB 10.x ve MySQL 8.x’te DEFAULT (CURRENT_DATE) gibi ifadeler de desteklenir:

CREATE TABLE abonelikler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    kullanici_id INT NOT NULL,
    baslangic_tarihi DATE DEFAULT (CURRENT_DATE),
    bitis_tarihi DATE DEFAULT (DATE_ADD(CURRENT_DATE, INTERVAL 30 DAY)),
    olusturma_zamani TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Mevcut Tabloya DEFAULT Ekleme ve Değiştirme

Canlı sistemlerde sıklıkla karşılaşılan senaryo: tablo zaten var ama bir sütuna DEFAULT eklemek ya da mevcut DEFAULT’u değiştirmek istiyorsunuz. ALTER TABLE ile bunu yapabilirsiniz:

-- Mevcut sutuna DEFAULT eklemek
ALTER TABLE kullanicilar
MODIFY COLUMN deneme_hakki INT DEFAULT 5;

-- DEFAULT degerini degistirmek
ALTER TABLE siparisler
ALTER COLUMN durum SET DEFAULT 'new';

-- DEFAULT degerini kaldirmak
ALTER TABLE siparisler
ALTER COLUMN durum DROP DEFAULT;

-- Yeni sutun eklerken DEFAULT tanimlamak
ALTER TABLE kullanicilar
ADD COLUMN son_giris DATETIME DEFAULT NULL AFTER kayit_tarihi;

Canlı üretim ortamında büyük tablolarda ALTER TABLE yaparken dikkatli olun. Milyonlarca satır içeren tablolarda bu işlem uzun sürebilir ve tablo kilitlenebilir. Bu durumda pt-online-schema-change veya gh-ost gibi araçlar kullanmanız önerilir.

DEFAULT Değerleri Sorgulamak

Bir tablonun hangi sütunlarında hangi DEFAULT değerlerin tanımlı olduğunu görmek için birkaç yöntem var:

-- SHOW COLUMNS ile
SHOW COLUMNS FROM kullanicilar;

-- DESCRIBE ile
DESCRIBE siparisler;

-- information_schema uzerinden daha detayli sorgu
SELECT
    COLUMN_NAME,
    COLUMN_DEFAULT,
    IS_NULLABLE,
    DATA_TYPE,
    COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'veritabaniniz'
  AND TABLE_NAME = 'kullanicilar'
ORDER BY ORDINAL_POSITION;

Bu sorguyu production monitoring scriptlerinize eklemek iyi bir pratiktir. Özellikle ekip büyüdükçe ve şema değişiklikleri sık yapılmaya başlandığında hangi alanlarda DEFAULT var, hangisinde yok takip etmek önemli hale gelir.

Gerçek Dünya Senaryosu: Log ve Audit Tabloları

Üretim ortamında sık kullandığım bir pattern: audit log tabloları. Bu tablolarda DEFAULT kullanımı hem zorunlu hem de hayat kurtarıcıdır:

CREATE TABLE audit_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    tablo_adi VARCHAR(64) NOT NULL,
    islem_tipi ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    kayit_id INT NOT NULL,
    eski_deger JSON DEFAULT NULL,
    yeni_deger JSON DEFAULT NULL,
    kullanici VARCHAR(100) DEFAULT 'SYSTEM',
    ip_adresi VARCHAR(45) DEFAULT '0.0.0.0',
    islem_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
    sunucu_adi VARCHAR(100) DEFAULT @@hostname,
    basarili TINYINT(1) DEFAULT 1,
    hata_mesaji TEXT DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Burada @@hostname sistem değişkenini DEFAULT olarak kullandık. Bu özellik özellikle master-slave veya Galera Cluster gibi çok sunuculu ortamlarda hangi node’un yazdığını takip etmek için çok işe yarar.

Şimdi bu tabloya nasıl veri yazıldığına bakalım:

-- Sadece zorunlu alanlari doldurarak log eklemek
INSERT INTO audit_log (tablo_adi, islem_tipi, kayit_id, yeni_deger)
VALUES ('kullanicilar', 'INSERT', 42, '{"kullanici_adi": "yeni_kullanici"}');

-- Sistem tarafindan tetiklenen bir islem icin
INSERT INTO audit_log (tablo_adi, islem_tipi, kayit_id)
VALUES ('siparisler', 'DELETE', 1337);
-- kullanici "SYSTEM" olarak, ip "0.0.0.0" olarak kaydedilecek

Gerçek Dünya Senaryosu: Çok Kiracılı SaaS Uygulaması

Bir SaaS platformunda farklı müşterilerin (tenant) farklı varsayılan ayarları olabilir. Bu durumu veritabanı seviyesinde yönetmek için DEFAULT kullanımı kritik önem taşır:

CREATE TABLE tenant_ayarlari (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT NOT NULL,
    max_kullanici INT DEFAULT 10,
    depolama_limiti_gb INT DEFAULT 5,
    para_birimi CHAR(3) DEFAULT 'TRY',
    dil VARCHAR(10) DEFAULT 'tr-TR',
    zaman_dilimi VARCHAR(50) DEFAULT 'Europe/Istanbul',
    email_bildirimleri TINYINT(1) DEFAULT 1,
    sms_bildirimleri TINYINT(1) DEFAULT 0,
    iki_faktoru_dogrulama TINYINT(1) DEFAULT 0,
    paket_tipi VARCHAR(20) DEFAULT 'free',
    olusturma_tarihi TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY (tenant_id)
);

-- Yeni tenant kaydi: sadece tenant_id vermek yeterli
INSERT INTO tenant_ayarlari (tenant_id) VALUES (1001);

-- Premium pakete gec: sadece degisen alanlari guncelle
UPDATE tenant_ayarlari
SET max_kullanici = 100,
    depolama_limiti_gb = 50,
    paket_tipi = 'premium'
WHERE tenant_id = 1001;

Bu yapıda yeni bir müşteri sisteme eklendiğinde tüm parametreleri doldurmak zorunda kalmıyorsunuz. Üstelik iş gereksinimi değiştiğinde tek bir ALTER TABLE ile tüm yeni müşterilerin varsayılan değerini güncelleyebiliyorsunuz.

DEFAULT ile NOT NULL Kombinasyonu

DEFAULT ve NOT NULL birlikte kullanıldığında çok güçlü bir veri bütünlüğü mekanizması oluşur. NULL değerlerin tabloya girmesini engellerken aynı zamanda INSERT sorgularını da sadeleştirirsiniz:

CREATE TABLE urun_stok (
    id INT AUTO_INCREMENT PRIMARY KEY,
    urun_kodu VARCHAR(20) NOT NULL,
    urun_adi VARCHAR(200) NOT NULL,
    stok_miktari INT NOT NULL DEFAULT 0,
    rezerve_miktar INT NOT NULL DEFAULT 0,
    kritik_stok_seviyesi INT NOT NULL DEFAULT 10,
    aktif TINYINT(1) NOT NULL DEFAULT 1,
    para_birimi CHAR(3) NOT NULL DEFAULT 'TRY',
    son_guncelleme TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Burada stok_miktari NOT NULL DEFAULT 0 kombinasyonu şunu garanti eder: bu sütun asla NULL olamaz ve değer girilmezse 0 olarak başlar. Bu, sonraki toplama/çıkarma işlemlerinde NULL kontrolü yapma zorunluluğunu ortadan kaldırır.

SHOW CREATE TABLE ile DEFAULT Değerleri Doğrulamak

Deployment süreçlerinde tablonun tam tanımını görüntülemek için:

SHOW CREATE TABLE kullanicilarG

-- Veya migration scriptlerini kontrol etmek icin
SHOW CREATE TABLE siparislerG

Bu komut CREATE TABLE ifadesinin tamamını döndürür ve hangi sütunda hangi DEFAULT değerin tanımlı olduğunu net şekilde gösterir. CI/CD pipeline’larında şema değişikliklerini doğrulamak için bu çıktıyı parse etmek oldukça yaygın bir pratiktir.

DEFAULT Değerler ve Stored Procedure Kombinasyonu

Stored procedure’larda DEFAULT değerleri okuyup mantıksal kararlar almak mümkündür. Örneğin bir kullanıcı oluşturma procedure’ı yazalım:

DELIMITER //

CREATE PROCEDURE kullanici_olustur(
    IN p_kullanici_adi VARCHAR(50),
    IN p_email VARCHAR(100),
    IN p_rol VARCHAR(20)
)
BEGIN
    DECLARE v_rol VARCHAR(20);

    -- Rol parametresi NULL geldiyse varsayilan deger kullan
    SET v_rol = COALESCE(p_rol, 'user');

    INSERT INTO kullanicilar (kullanici_adi, email, rol)
    VALUES (p_kullanici_adi, p_email, v_rol);

    -- Olusturulan kaydin bilgilerini dondur
    SELECT
        id,
        kullanici_adi,
        email,
        rol,
        aktif,
        kayit_tarihi
    FROM kullanicilar
    WHERE id = LAST_INSERT_ID();
END //

DELIMITER ;

-- Kullanimi
CALL kullanici_olustur('ali_veli', '[email protected]', NULL);
CALL kullanici_olustur('admin_user', '[email protected]', 'admin');

MariaDB’ye Özgü DEFAULT Özellikleri

MariaDB, MySQL’e kıyasla bazı ek DEFAULT özellikleri sunar. Özellikle MariaDB 10.2+ sürümlerinden itibaren fonksiyon tabanlı DEFAULT değerler desteklenmektedir:

-- MariaDB 10.2+ ozellikleri
CREATE TABLE belgeler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    baslik VARCHAR(200) NOT NULL,
    icerik LONGTEXT,
    kelime_sayisi INT DEFAULT 0,
    uuid CHAR(36) DEFAULT (UUID()),
    hash_deger VARCHAR(64) DEFAULT (SHA2(RAND(), 256)),
    olusturma_tarihi DATETIME(6) DEFAULT (NOW(6)),
    yil_ay VARCHAR(7) DEFAULT (DATE_FORMAT(NOW(), '%Y-%m'))
);

MySQL 8.0.13+ sürümünde de fonksiyon tabanlı DEFAULT değerler geldi ama MariaDB bu özelliği daha erken sundu. Özellikle UUID üretimi için uygulama katmanına ihtiyaç duymadan veritabanı seviyesinde UUID atamak ciddi bir kolaylık sağlar.

Yaygın Hatalar ve Dikkat Edilmesi Gerekenler

Pratikte sık karşılaşılan sorunlar:

  • BLOB/TEXT sütunlarında DEFAULT: MySQL’in eski sürümlerinde BLOB ve TEXT sütunlarına DEFAULT değer atamak desteklenmiyordu. MariaDB 10.2.1+ ve MySQL 8.0.13+ ile bu kısıtlama kalktı. Eski sürümlerde DEFAULT NULL dışında bir değer atamaya çalışırsanız hata alırsınız.
  • ENUM tipinde DEFAULT: ENUM sütunlarında DEFAULT değerin ENUM listesinde tanımlı olması zorunludur, aksi halde hata alırsınız.
  • Sıfır tarih sorunu: DEFAULT '0000-00-00' gibi sıfır tarih kullanımı artık önerilmiyor. sql_mode ayarına bağlı olarak hata verebilir. Bunun yerine DEFAULT NULL tercih edin.
  • Büyük/küçük harf duyarlılığı: VARCHAR alanlarda DEFAULT değer büyük/küçük harf duyarlı değildir ama collation ayarlarına bağlı olarak karşılaştırmalarda fark yaratabilir.
-- Yanlis kullanim - eski MySQL surumlerinde hata verir
-- CREATE TABLE test (icerik TEXT DEFAULT 'bos');

-- Dogru kullanim
CREATE TABLE test_dogru (
    id INT AUTO_INCREMENT PRIMARY KEY,
    icerik TEXT DEFAULT NULL,
    durum ENUM('aktif', 'pasif', 'beklemede') DEFAULT 'aktif',
    tarih DATE DEFAULT NULL  -- sifir tarih yerine NULL kullanin
);

DEFAULT Değerleri Uygulama Katmanıyla Senkronize Tutmak

Büyük ekiplerde sık yaşanan sorun: veritabanındaki DEFAULT değer değiştirilmiş ama ORM veya uygulama katmanındaki varsayılan değer güncellenmemiş. Bu tür tutarsızlıklar sessiz sedasız veri bozulmalarına yol açar.

Bunu önlemek için:

  • Migration araçları kullanın: Flyway, Liquibase veya Laravel’in migration sistemi gibi araçlar şema değişikliklerini kod olarak yönetmenizi sağlar
  • Schema versioning yapın: Her değişikliği versiyon kontol sisteminde takip edin
  • Periyodik validasyon çalıştırın: information_schema sorgusuyla beklenen DEFAULT değerlerin hala yerinde olup olmadığını kontrol eden scriptler yazın
-- Kritik tablolarda DEFAULT degerlerini dogrulayan kontrol sorgusu
SELECT
    TABLE_NAME,
    COLUMN_NAME,
    COLUMN_DEFAULT,
    IS_NULLABLE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'production_db'
  AND TABLE_NAME IN ('kullanicilar', 'siparisler', 'urun_stok')
  AND COLUMN_DEFAULT IS NOT NULL
ORDER BY TABLE_NAME, ORDINAL_POSITION;

Bu sorguyu cron job’a bağlayıp çıktıyı bir referans dosyasıyla karşılaştıran basit bir shell scripti bile sizi pek çok baş ağrısından kurtarır.

Sonuç

DEFAULT değer ataması, çoğu zaman küçük bir detay gibi görünse de veritabanı tasarımının temel taşlarından biridir. Doğru kullanıldığında uygulama kodunu sadeleştirir, veri bütünlüğünü güçlendirir ve bakım maliyetini düşürür. Yanlış veya eksik kullanıldığında ise sessiz veri hataları ve tutarsızlıklar kaçınılmaz olur.

Özellikle dikkat etmeniz gereken nokta şu: DEFAULT değerleri bir kez tanımlayıp unutmak yerine periyodik olarak gözden geçirmek ve iş gereksinimlerine uygunluğunu kontrol etmek gerekir. Bir SaaS platformunda “ücretsiz plan 10 kullanıcı” olan kural değiştiğinde hem uygulama kodunu hem de veritabanındaki DEFAULT değeri güncellemeyi unutmak, yeni kayıtların yanlış limitle oluşmasına neden olur.

MariaDB ve MySQL’in sunduğu fonksiyon tabanlı DEFAULT’lar, UUID üretimi ve timestamp yönetimi gibi konularda uygulama katmanına olan bağımlılığı azaltır. Bu özellikleri yerinde kullanmak, özellikle mikroservis mimarilerinde farklı dillerde yazılmış servislerin aynı veritabanını paylaştığı durumlarda tutarlılığı merkezi olarak sağlamanın en pratik yoludur.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir