MariaDB ve MySQL’de BETWEEN ile Aralık Sorgusu Kullanımı
Veritabanı sorgularında en çok ihtiyaç duyulan işlemlerden biri belirli bir aralıktaki verileri çekmektir. Tarihler arasında sipariş aramak, belirli fiyat aralığındaki ürünleri listelemek ya da yaş gruplarına göre kullanıcıları filtrelemek… Bunların hepsinde BETWEEN operatörü hayat kurtarır. MySQL ve MariaDB’de BETWEEN kullanımını bilmek, hem sorgu okunabilirliğini artırır hem de daha temiz kod yazmanızı sağlar. Bu yazıda gerçek dünya senaryolarıyla BETWEEN operatörünü her açıdan ele alacağız.
BETWEEN Nedir ve Neden Kullanmalısınız?
BETWEEN operatörü, bir değerin iki sınır değer arasında olup olmadığını kontrol eder. Teknik olarak şu karşılaştırmanın kısaltmasıdır:
-- Bu iki sorgu tamamen aynı sonucu verir
SELECT * FROM urunler WHERE fiyat >= 100 AND fiyat <= 500;
SELECT * FROM urunler WHERE fiyat BETWEEN 100 AND 500;
Gördüğünüz gibi BETWEEN kullanmak kodu hem daha kısa hem de daha okunabilir yapıyor. Özellikle başka kişilerin de bakacağı production veritabanlarında ve stored procedure’lerde bu okunabilirlik ciddi fark yaratır.
Önemli bir detay: BETWEEN her iki uç değeri de dahil eder. Yani BETWEEN 100 AND 500 dediğinizde 100 ve 500 değerleri de sonuçlara dahildir. Buna inclusive (kapsayıcı) davranış denir.
Temel Kullanım Sözdizimi
SELECT kolon1, kolon2
FROM tablo_adi
WHERE kolon_adi BETWEEN deger1 AND deger2;
Burada dikkat edilmesi gereken nokta, deger1 her zaman deger2‘den küçük veya eşit olmalıdır. Aksi hâlde sorgu hata vermez ama hiç sonuç döndürmez. Bu sessiz başarısızlık durumu production’da kafanızı karıştırabilir.
Sayısal Aralık Sorguları
En klasik kullanım şekli sayısal değerler üzerindedir. Bir e-ticaret sisteminde belirli fiyat aralığındaki ürünleri çekmek istediğinizi düşünün:
-- Fiyatı 50 ile 250 TL arasındaki ürünleri listele
SELECT
urun_id,
urun_adi,
fiyat,
stok_miktari
FROM urunler
WHERE fiyat BETWEEN 50.00 AND 250.00
ORDER BY fiyat ASC;
Benzer şekilde stok miktarına göre filtreleme yapabilirsiniz. Örneğin kritik stok seviyesi izleme için:
-- Stok miktarı 10 ile 50 arasında olan ürünleri bul (kritik eşik bölgesi)
SELECT
urun_adi,
stok_miktari,
tedarikci_id,
son_siparis_tarihi
FROM urunler
WHERE stok_miktari BETWEEN 10 AND 50
ORDER BY stok_miktari ASC;
Bu sorgu sayesinde ne çok az ne de çok fazla stokta olan, yani dikkat gerektiren ürünleri hızlıca tespit edebilirsiniz.
Tarih ve Zaman Aralığı Sorguları
BETWEEN operatörünün en sık kullanıldığı alan kesinlikle tarih sorgulamalarıdır. Log analizi, raporlama, sipariş takibi… Hepsinde tarih aralığı filtrelemesi şart.
-- Belirli tarih aralığındaki siparişleri çek
SELECT
siparis_id,
musteri_id,
toplam_tutar,
siparis_tarihi,
durum
FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01' AND '2024-03-31'
ORDER BY siparis_tarihi DESC;
Yukarıdaki sorgu 2024 yılının ilk çeyreğindeki tüm siparişleri getirir. Ancak burada dikkatli olunması gereken kritik bir nokta var.
Tarih-Zaman (DATETIME) Sütunlarında Dikkat Edilmesi Gereken Nokta
Eğer sütununuz DATE değil DATETIME tipindeyse ve bitiş günü için sadece tarih yazarsanız, o günün başlangıcını (00:00:00) baz alır. Yani son gün dahil olmayabilir:
-- YANLIS: 2024-03-31 tarihli kayıtların büyük kısmı eksik gelir
SELECT * FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01' AND '2024-03-31';
-- DOGRU: Günün sonuna kadar olan kayıtları almak için
SELECT * FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01 00:00:00' AND '2024-03-31 23:59:59';
Ya da daha pratik bir yaklaşım olarak DATE() fonksiyonunu kullanabilirsiniz:
-- DATE() fonksiyonu ile daha temiz çözüm
SELECT
siparis_id,
musteri_id,
toplam_tutar,
siparis_tarihi
FROM siparisler
WHERE DATE(siparis_tarihi) BETWEEN '2024-01-01' AND '2024-03-31'
ORDER BY siparis_tarihi;
Ancak dikkat: DATE() fonksiyonu indeks kullanımını engelleyebilir. Yoğun trafikli tablolarda bu performans sorununa yol açar. Production’da büyük tablolar için şu yaklaşım daha sağlıklıdır:
-- Index dostu tarih aralığı sorgusu
SELECT
siparis_id,
musteri_id,
toplam_tutar,
siparis_tarihi
FROM siparisler
WHERE siparis_tarihi >= '2024-01-01 00:00:00'
AND siparis_tarihi < '2024-04-01 00:00:00';
NOT BETWEEN ile Aralık Dışı Sorgular
BETWEEN‘in tam tersi olan NOT BETWEEN ile bir aralığın dışındaki değerleri alabilirsiniz:
-- Fiyatı 100 ile 500 TL arasında OLMAYAN ürünler
SELECT
urun_adi,
fiyat,
kategori
FROM urunler
WHERE fiyat NOT BETWEEN 100 AND 500
ORDER BY fiyat;
Bu sorgu hem 100 TL’nin altındaki hem de 500 TL’nin üzerindeki ürünleri getirir. Kampanya dışı ürünleri listelemek ya da anomali tespiti yapmak için oldukça kullanışlıdır.
JOIN ile Birlikte BETWEEN Kullanımı
Gerçek dünya senaryolarında nadiren tek tablo ile çalışırsınız. BETWEEN operatörünü JOIN sorgularıyla birleştirmek çok güçlü sonuçlar verir:
-- Belirli tarih aralığında sipariş veren müşterilerin detayları
SELECT
m.musteri_adi,
m.musteri_soyadi,
m.email,
COUNT(s.siparis_id) AS siparis_sayisi,
SUM(s.toplam_tutar) AS toplam_harcama
FROM musteriler m
INNER JOIN siparisler s ON m.musteri_id = s.musteri_id
WHERE s.siparis_tarihi BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY m.musteri_id, m.musteri_adi, m.musteri_soyadi, m.email
HAVING COUNT(s.siparis_id) > 3
ORDER BY toplam_harcama DESC;
Bu sorgu 2024 yılında 3’ten fazla sipariş veren müşterileri toplam harcamalarına göre sıralar. Sadık müşteri analizi için birebir bir sorgu.
Metin (VARCHAR/CHAR) Sütunlarda BETWEEN
BETWEEN operatörü metin değerlerle de çalışır, ancak karşılaştırma alfabetik sıraya göre yapılır. Bu bazen beklenmedik sonuçlar doğurabilir:
-- A ile M harfi arasında başlayan müşteri adları
SELECT musteri_adi, musteri_soyadi
FROM musteriler
WHERE musteri_soyadi BETWEEN 'A' AND 'N'
ORDER BY musteri_soyadi;
Burada dikkat: 'N' ile başlayan soyadlar dahil edilir, ancak 'N' harfinin kendisi tek karakter olduğu için 'Na', 'Ne' gibi değerler 'N' AND 'N' aralığını aşar. Bu yüzden metin aralıklarında son değeri dikkatli belirlemelisiniz.
Metin tabanlı BETWEEN kullanımı pratikte daha çok durum kodları ve kategorik sütunlarda işe yarar:
-- Durum kodu 'A' ile 'D' arasındaki kayıtlar
SELECT kayit_id, aciklama, durum_kodu
FROM is_emirleri
WHERE durum_kodu BETWEEN 'A' AND 'D'
ORDER BY durum_kodu, kayit_id;
Gerçek Dünya Senaryosu: Log Analizi
Sistem yöneticisi olarak en sık yapacağınız işlerden biri belirli zaman aralığındaki log kayıtlarını analiz etmektir. Veritabanına aktarılmış uygulama logları için:
-- Son 24 saat içindeki hata loglarını çek
SELECT
log_id,
log_seviyesi,
uygulama_adi,
mesaj,
sunucu_adi,
olusturma_zamani
FROM uygulama_loglari
WHERE olusturma_zamani BETWEEN NOW() - INTERVAL 24 HOUR AND NOW()
AND log_seviyesi IN ('ERROR', 'CRITICAL')
ORDER BY olusturma_zamani DESC
LIMIT 100;
INTERVAL kullanımı dinamik tarih aralıkları için mükemmeldir. Sabit tarih yazmak yerine NOW() ile anlık zaman damgası kullanmak, bu sorguyu bir cron job’a ya da monitoring sistemine bağlamanızı kolaylaştırır.
-- Geçen hafta ile bu hafta karşılaştırmalı log sayısı
SELECT
uygulama_adi,
log_seviyesi,
COUNT(*) AS log_sayisi,
DATE_FORMAT(olusturma_zamani, '%Y-%U') AS hafta
FROM uygulama_loglari
WHERE olusturma_zamani BETWEEN NOW() - INTERVAL 14 DAY AND NOW()
GROUP BY uygulama_adi, log_seviyesi, DATE_FORMAT(olusturma_zamani, '%Y-%U')
ORDER BY uygulama_adi, log_seviyesi, hafta;
BETWEEN ile Performans Optimizasyonu
BETWEEN operatörü doğru indeks yapısıyla çok hızlı çalışır. Ancak indeks olmadan büyük tablolarda full table scan yapacaktır.
Tarih ya da sık sorgulanan sayısal sütunlar için indeks oluşturun:
-- Siparis tarihine indeks ekle
ALTER TABLE siparisler ADD INDEX idx_siparis_tarihi (siparis_tarihi);
-- Bileşik indeks (önce tarih, sonra durum)
ALTER TABLE siparisler
ADD INDEX idx_tarih_durum (siparis_tarihi, durum);
-- Sorgunun indeks kullanıp kullanmadığını kontrol et
EXPLAIN SELECT *
FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01' AND '2024-06-30';
EXPLAIN çıktısında type sütununda range yazıyorsa, BETWEEN sorgunuz indeksi kullanıyor demektir. Eğer ALL yazıyorsa full table scan yapılıyor ve indeks eklemeniz gerekiyor.
Subquery ile BETWEEN Kullanımı
Bazen aralık sınırlarını dinamik olarak başka bir sorgudan almak gerekebilir:
-- Ortalama fiyatın yüzde 20 altı ile üstü arasındaki ürünler
SELECT
urun_adi,
fiyat,
kategori
FROM urunler
WHERE fiyat BETWEEN
(SELECT AVG(fiyat) * 0.80 FROM urunler)
AND
(SELECT AVG(fiyat) * 1.20 FROM urunler)
ORDER BY fiyat;
Bu sorgu, ortalama fiyatın yüzde yirmi bandında yer alan ürünleri listeler. Kampanya fiyatlandırması ya da anomali tespiti için kullanışlıdır.
Daha temiz yazım için CTE (Common Table Expression) kullanabilirsiniz:
-- CTE ile daha okunabilir versiyon
WITH fiyat_sinirlari AS (
SELECT
AVG(fiyat) * 0.80 AS alt_sinir,
AVG(fiyat) * 1.20 AS ust_sinir
FROM urunler
WHERE aktif = 1
)
SELECT
u.urun_adi,
u.fiyat,
u.kategori
FROM urunler u, fiyat_sinirlari fs
WHERE u.fiyat BETWEEN fs.alt_sinir AND fs.ust_sinir
AND u.aktif = 1
ORDER BY u.fiyat;
CTE yaklaşımı hem daha okunabilir hem de alt sorgunun bir kez çalışmasını sağlar.
Stored Procedure İçinde BETWEEN
Tekrarlayan aralık sorgularını stored procedure’e almak hem güvenli hem de yönetilebilir bir yapı oluşturur:
-- Tarih aralığına göre satış raporu oluşturan procedure
DELIMITER //
CREATE PROCEDURE satis_raporu(
IN baslangic_tarihi DATE,
IN bitis_tarihi DATE,
IN min_tutar DECIMAL(10,2)
)
BEGIN
-- Parametre doğrulama
IF baslangic_tarihi > bitis_tarihi THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Baslangic tarihi bitis tarihinden buyuk olamaz';
END IF;
SELECT
s.siparis_id,
CONCAT(m.musteri_adi, ' ', m.musteri_soyadi) AS musteri,
s.toplam_tutar,
s.siparis_tarihi,
s.durum
FROM siparisler s
INNER JOIN musteriler m ON s.musteri_id = m.musteri_id
WHERE s.siparis_tarihi BETWEEN baslangic_tarihi AND bitis_tarihi
AND s.toplam_tutar >= min_tutar
ORDER BY s.toplam_tutar DESC;
END //
DELIMITER ;
-- Kullanımı
CALL satis_raporu('2024-01-01', '2024-03-31', 500.00);
Bu stored procedure parametre doğrulaması da yaparak hatalı kullanımı önler.
Sık Yapılan Hatalar ve Çözümleri
Ters sıralı aralık: BETWEEN 500 AND 100 yazmak hata vermez ama hiç sonuç döndürmez. Her zaman küçük değeri önce yazın.
NULL değerler: BETWEEN operatörü NULL değerleri dahil etmez. NULL değerleri de dahil etmek istiyorsanız:
-- NULL degerleri de dahil eden sorgu
SELECT urun_adi, fiyat
FROM urunler
WHERE fiyat BETWEEN 100 AND 500
OR fiyat IS NULL;
Saat dilimi sorunları: Sunucu ve uygulama farklı saat dilimlerinde çalışıyorsa tarih sorguları yanlış sonuç verebilir. Sorgu öncesi saat dilimini kontrol edin:
-- Mevcut saat dilimini kontrol et
SELECT @@global.time_zone, @@session.time_zone;
-- Gerekirse oturum saat dilimini ayarla
SET time_zone = '+03:00';
-- Ardından BETWEEN sorgunu çalıştır
SELECT * FROM etkinlikler
WHERE etkinlik_tarihi BETWEEN '2024-06-01' AND '2024-06-30';
Veri tipi uyumsuzluğu: Sütun integer iken string değerle karşılaştırmak implicit conversion yapar ve indeksi etkisiz kılabilir. Veri tiplerinin her zaman eşleştiğinden emin olun.
BETWEEN Yerine Hangi Durumlarda Farklı Yöntem Kullanmalısınız?
BETWEEN her zaman doğru seçim değildir:
- Aralık dışlayıcı olması gerektiğinde:
BETWEENher iki ucu da dahil eder. Sadece başlangıç dahil, bitiş hariç bir aralık istiyorsanız>= AND <kullanın. - Çok sayıda ayrık değer için: 5-10 farklı değer kontrol etmeniz gerekiyorsa
INoperatörü daha uygundur. - Dinamik alt sorgu sınırlarında: Bazen CTE ya da JOIN ile hesaplanan sınırlar
BETWEEN‘den daha performanslı olabilir. - LIKE ile metin arama için: Metin içinde belirli kalıplar arıyorsanız
BETWEENdeğilLIKEya daREGEXPkullanın.
Sonuç
BETWEEN operatörü MySQL ve MariaDB’nin en temiz ve en okunaklı filtreleme araçlarından biridir. Sayısal değerler, tarihler ve metinler üzerinde çalışabilmesi, NOT BETWEEN ile terslenebilmesi ve diğer SQL bileşenleriyle uyumlu çalışması onu çok yönlü bir seçenek yapar.
Pratikte en çok tarih aralığı sorgularında kullanacaksınız. Burada dikkat etmeniz gereken en kritik nokta DATETIME sütunlarında saat bilgisini göz ardı etmemektir. Production sistemlerde sorgu yazmadan önce EXPLAIN ile yürütme planını kontrol etme alışkanlığı edinmek uzun vadede ciddi performans sorunlarının önüne geçer.
Stored procedure içinde kullanırken parametre doğrulaması eklemek, hem güvenliği artırır hem de hata ayıklamayı kolaylaştırır. Büyük tablolarda ilgili sütunlara indeks eklemek ise BETWEEN sorgularının gerçek potansiyeline ulaşması için şarttır.
Veritabanı yönetiminde basit görünen ama içinde pek çok detay barındıran BETWEEN operatörünü doğru kullanmak, hem sorgu performansınızı hem de kod kalitenizi gözle görülür biçimde iyileştirir.
