MySQL'de ADDDATE() ve SUBDATE() ile Tarih Aritmetiği

Selamlar, bu yazımda MySQL'in en sık karşımıza çıkan ama bir o kadar da kafa karıştırabilen iki fonksiyonuna, ADDDATE() ve SUBDATE()'e bakacağız. Tarih aritmetiği görünüşte basit bir konu; ne var ki MySQL'in iki ayrı kullanım şekli sunması, bir de aynı işi yapan DATE_ADD() / DATE_SUB()'ın varlığı, yeni başlayanı haklı olarak duraklatıyor. Hadi kafa karışıklığını dağıtalım.

ADDDATE() ne işe yarar?

ADDDATE() bir tarihe belirli bir süre ekler. İki farklı çağırma biçimi var: ya INTERVAL ifadesiyle ay, hafta, saat gibi birimler verirsiniz, ya da ikinci argümana düz bir tam sayı geçip 'şu kadar gün ekle' dersiniz. İkincisi, yani kısa-yol biçimi, ADDDATE()'in DATE_ADD()'den ayrıldığı tek nokta.

-- Tam INTERVAL söz dizimi (DATE_ADD ile aynı)
ADDDATE(tarih, INTERVAL ifade birim)

-- Kısa-yol: N gün ekle
ADDDATE(tarih, gun_sayisi)

Birkaç örnekle netleştirelim:

-- 10 gun ekle (kisa-yol)
SELECT ADDDATE('2026-05-08', 10);
-- Sonuc: '2026-05-18'

-- 3 ay ekle
SELECT ADDDATE('2026-05-08', INTERVAL 3 MONTH);
-- Sonuc: '2026-08-08'

-- 2 saat 30 dakika ekle
SELECT ADDDATE('2026-05-08 09:00:00', INTERVAL '2:30' HOUR_MINUTE);
-- Sonuc: '2026-05-08 11:30:00'

-- Negatif tam sayi: aslinda cikarma yapar
SELECT ADDDATE('2026-05-08', -7);
-- Sonuc: '2026-05-01'

Son örnek ilginç: ikinci argümana negatif değer verince fonksiyon sessizce çıkarma moduna geçiyor. Bence bu pratik ama kodunuzu okuyan biri için niyetinizi gizleyen bir kullanım; tarih çıkarmak istiyorsanız doğrudan SUBDATE() çağırın, okunurluk artar.

SUBDATE() tarafı

SUBDATE() aynı mantığın ters yüzü. INTERVAL'la her birimi çıkarır, ya da kısa-yol biçiminde gün sayısı alır.

-- 10 gun cikar
SELECT SUBDATE('2026-05-08', 10);
-- Sonuc: '2026-04-28'

-- 1 ay cikar
SELECT SUBDATE('2026-05-08', INTERVAL 1 MONTH);
-- Sonuc: '2026-04-08'

-- Bugunden 90 gun once
SELECT SUBDATE(NOW(), 90);

SUBDATE(NOW(), 90) tarzı kullanımlar raporlama sorgularında çok işinize yarar. Aşağıda buna döneceğiz.

Peki DATE_ADD()'den farkı ne?

Şöyle ki: ADDDATE(tarih, INTERVAL ...) ile DATE_ADD(tarih, INTERVAL ...) birebir aynı şey. Tek fark, ADDDATE()'in tam sayı kısa-yol biçimini de kabul etmesi.

-- Bu ucu de ayni sonucu verir
SELECT ADDDATE('2026-05-08', 5);
SELECT ADDDATE('2026-05-08', INTERVAL 5 DAY);
SELECT DATE_ADD('2026-05-08', INTERVAL 5 DAY);
-- Hepsi: '2026-05-13'

Bana sorarsanız ekipte tek tip kullanım belirleyin: ya hep DATE_ADD / DATE_SUB kullanın (birim her zaman açık görünür), ya da ADDDATE / SUBDATE'e geçin ve gün sayısı söz konusu olduğunda kısa-yolu serbest bırakın. Aynı kod tabanında ikisinin karışması okumayı zorlaştırıyor.

Pratik: son tarih hesabı

Görev tablosunda her satıra ait bir 'kaç gün içinde teslim' alanı varsa, son tarihi anlık hesaplamak için ADDDATE() biçilmiş kaftan:

CREATE TABLE gorevler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    gorev_adi VARCHAR(100),
    olusturma_tarihi DATE,
    sure_gun INT
);

INSERT INTO gorevler (gorev_adi, olusturma_tarihi, sure_gun) VALUES
('Rapor yaz',         '2026-05-08', 7),
('Kod incele',        '2026-05-08', 3),
('Prod yayina al',    '2026-05-08', 14);

SELECT
    gorev_adi,
    olusturma_tarihi,
    ADDDATE(olusturma_tarihi, sure_gun) AS son_tarih
FROM gorevler;

Burada ikinci argüman hard-coded değil, doğrudan kolondan geliyor. Bu, kısa-yol biçiminin gerçekten kazandığı yer; INTERVAL ile aynısını yapmak için dinamik SQL'e gerek duyardınız.

Raporlama pencereleri

Son N gün, önümüzdeki N gün gibi pencereler hemen her dashboard sorgusunda var:

-- Son 30 gun siparis
SELECT * FROM siparisler
WHERE siparis_tarihi >= SUBDATE(CURDATE(), 30);

-- Onumuzdeki 7 gun randevu
SELECT * FROM randevular
WHERE randevu_tarihi BETWEEN CURDATE() AND ADDDATE(CURDATE(), 7);

Diğer tarih fonksiyonlarıyla da güzel bestelenir; mesela 'üç ay sonraki ayın son günü' tek satırda çıkar:

SELECT LAST_DAY(ADDDATE(CURDATE(), INTERVAL 3 MONTH)) AS gelecek_ay_sonu;

Sık karşılaşılan tuzaklar

  • Negatif tam sayıyla ADDDATE() kullanmak: Çalışır ama okuyucuyu yanıltır. Niyetiniz çıkarmaksa SUBDATE() yazın.
  • NULL beklemediğiniz yerden gelir: ADDDATE(NULL, 10) ya da ADDDATE(tarih, NULL) her zaman NULL döner. Kolondan gelen değerler için COALESCE ile bir varsayılan koymak çoğu zaman iyi fikir.
  • Ay sonu sürprizleri: ADDDATE('2026-01-31', INTERVAL 1 MONTH) size 2026-02-28 verir. MySQL ay sonunu otomatik kırpar; bu davranışın farkında olmak iş kuralınızla uyumsuzluğa düşmenizi engeller.
  • Index'siz tarih aritmetiği: WHERE ADDDATE(kolon, 30) > NOW() yazarsanız index kullanılmaz. Aritmetiği sabit tarafa taşıyın: WHERE kolon > SUBDATE(NOW(), 30). Klasik tuzak ama hâlâ sıkça gördüğüm bir şey.

Kapanış

ADDDATE() ve SUBDATE(), MySQL tarih aritmetiğinin günlük araçları; INTERVAL ile esnek, tam sayı kısa-yoluyla pratik. Şahsi kanaatim, ekip içinde tek bir biçim seçip ona sadık kalmak en büyük kazancı veriyor; ondan sonra fonksiyonlar sessizce işini görüyor zaten. Umarım faydalı olur, görüşmek üzere.