SQL'de Tarih Aralığına Göre Filtreleme (BETWEEN Tuzağı Dahil)
Bu ay, son 30 gün, geçen çeyrek: SQL'de tarih aralığı sorgulamanın güvenli yolu. BETWEEN'in saat bilgisiyle neden yanıltıcı olduğu ve PostgreSQL, SQL Server, MySQL, Oracle karşılıkları.
"Bu ayki siparişler", "son 30 gün", "geçen çeyreğin cirosu". Raporların neredeyse tamamı bir tarih aralığıyla başlar. Kulağa basit gelir, ama tarih kolonu saat bilgisi de tutuyorsa en sık yapılan hata tam burada saklıdır. Önce doğru kalıbı, sonra dört veritabanının kendine has fonksiyonlarını görelim.
Neden BETWEEN kullanmamalıyım?
Çok kişi "ocak ayı" için BETWEEN '2026-01-01' AND '2026-01-31' yazar. Kolon yalnızca tarihse sorun yok. Ama kolon zaman damgasıysa (timestamp), 31 Ocak saat 09:00'daki bir sipariş 2026-01-31 00:00:00 sınırının dışında kalır ve sessizce kaybolur. BETWEEN her iki ucu da dahil ettiği için bu tuzak fark edilmeden aylarca sürebilir.
Güvenli kalıp "yarı-açık aralık"tır: başlangıç dahil, bitiş hariç. Ayın ilk gününden bir sonraki ayın ilk gününe kadar, ama sonuncusu hariç:
SELECT *
FROM siparisler
WHERE tarih >= DATE '2026-01-01'
AND tarih < DATE '2026-02-01';Son 30 günü nasıl yazarım?
Sabit tarih yerine "bugünden geriye" bir aralık istediğinizde her veritabanının kendi "bugün" ve "gün çıkar" fonksiyonu devreye girer. Aynı sorgunun dört karşılığı:
-- PostgreSQL
WHERE tarih >= CURRENT_DATE - INTERVAL '30 days'
-- SQL Server
WHERE tarih >= DATEADD(DAY, -30, CAST(GETDATE() AS date))
-- MySQL
WHERE tarih >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
-- Oracle
WHERE tarih >= TRUNC(SYSDATE) - 30Bu ay, bu yıl gibi "içinde bulunduğumuz" dönemler
İçinde bulunduğunuz ayın başını hesaplamak için PostgreSQL'de date_trunc pratiktir; sonraki ay için bir ay eklersiniz:
-- PostgreSQL: içinde bulunduğumuz ay
WHERE tarih >= date_trunc('month', CURRENT_DATE)
AND tarih < date_trunc('month', CURRENT_DATE) + INTERVAL '1 month';SQL Server'da ayın son gününü EOMONTH verir; MySQL'de LAST_DAY vardır. Yine de en sağlam yaklaşım, ay sonunu "dahil" etmek yerine bir sonraki ayın başını "hariç" sınır olarak kullanmaktır; böylece saat bilgisi sizi hiç uğraştırmaz.
Saat dilimi (timezone) beni nasıl yakalar?
Kolon UTC saklıyor, siz yerel saate göre "bugün" diyorsanız, gece yarısına yakın kayıtlar bir gün kayabilir. Kural olarak, aralığı hesaplarken hem sınırların hem de kolonun aynı saat diliminde olduğundan emin olun. Bu ayrıntı özellikle günlük panolarda "sayılar neden birer gün geç?" sorusunun baş sorumlusudur.
Sık sorulan sorular
- BETWEEN tarih aralıklarında hiç kullanılmaz mı?
- Kolon yalnızca tarih (saatsiz) tutuyorsa BETWEEN güvenlidir ve okunur. Kolon zaman damgası (timestamp/datetime) ise ">= başlangıç AND < bir sonraki gün" kalıbını tercih edin; BETWEEN'in üst sınırı dahil etmesi orada sessiz veri kaybına yol açar.
- Tarihi metin olarak karşılaştırabilir miyim?
- Önerilmez. Tarihi '2026-01-01' gibi metinle karşılaştırmak biçim uyumuna bel bağlar ve indeksten yararlanmayı zorlaştırır. Tarih türüne çevirip (DATE, CAST, TO_DATE) tarih olarak karşılaştırmak hem doğru hem hızlıdır.
- Yıl ya da aya göre gruplarken aralık filtresi de gerekir mi?
- Genelde evet. Önce WHERE ile ilgilendiğiniz aralığı süzer, sonra GROUP BY ile yıla/aya göre toplarsınız. Aralığı filtrelemeden tüm tabloyu gruplamak hem yavaştır hem de istemediğiniz dönemleri sonuca katar.
- "Geçen çeyrek" gibi dönemleri nasıl hesaplarım?
- Çeyreğin başlangıç ve bitiş tarihini hesaplayıp yine yarı-açık aralık olarak verirsiniz. PostgreSQL'de date_trunc('quarter', ...) bu işi kolaylaştırır; diğer veritabanlarında çeyreğin ilk ayını elle hesaplarsınız.
Hangi tarih fonksiyonunun hangi motora ait olduğuyla uğraşmak istemezseniz, PerSight'a "bu ayki siparişler" ya da "son 30 günün gününe göre cirosu" demeniz yeterli. Bağlı olduğunuz veritabanına uygun sorguyu o kurar, siz yalnızca sonucu okursunuz.
Kaynaklar
İlgili yazılar
- SQL GROUP BY: Gruplayıp Toplamayı AnlamakAylık ciro, müşteri başına sipariş, kategoriye göre adet: GROUP BY ile toplama. COUNT/SUM/AVG, WHERE ile HAVING farkı ve MySQL'in ONLY_FULL_GROUP_BY tuzağı, gerçek örneklerle.
- Her Grupta En Yüksek N Kayıt: SQL'de Top-N per GroupHer kategoride en pahalı 3 ürün, her müşterinin son siparişi: grup başına en iyi N kaydı ROW_NUMBER ile bulmak. Pencere fonksiyonu kalıbı ve MySQL 5.7 için alternatif.
- Doğal Dille Veritabanı Sorgulama Nasıl Çalışır (ve Görünür Sorgu Neden Önemli)Doğal dilden SQL üretimi sade bir anlatımla: sorunuz nasıl sorguya dönüşür, salt-okunur ve her zaman görünür bir sorgu neden daha güvenli ve PerSight verinizi neden makinenizde tutar.