MySQL ve MariaDB’de LIMIT ile Sorgu Sonuçlarını Kısıtlama
Veritabanı sorgularında en sık yapılan hatalardan biri, büyük tablolara herhangi bir kısıtlama koymadan SELECT * çekmektir. Milyonlarca satır içeren bir tabloda bu tür sorgular hem veritabanı sunucusunu yorar hem de uygulamanın belleğini gereksiz yere doldurur. İşte tam bu noktada LIMIT ifadesi devreye girer ve bize son derece pratik bir çözüm sunar.
LIMIT Nedir ve Neden Kullanılır?
LIMIT, bir SQL sorgusunun döndüreceği satır sayısını kısıtlamak için kullanılan bir ifadedir. MySQL ve MariaDB’de bu ifade özellikle performans optimizasyonu, sayfalama (pagination) ve veri örnekleme gibi senaryolarda kritik bir rol oynar.
Bir sysadmin olarak şunu çok iyi bilirsiniz: Üretim ortamında çalışan bir veritabanı sunucusuna yapılan kontrolsüz sorgular, tüm sistemi yavaşlatabilir. Log tabloları, olay kayıtları veya kullanıcı aktivite tabloları zamanla milyonlarca satıra ulaşır. Bu tablolara LIMIT kullanmadan yapılan bir sorgu, disk I/O’yu patlatır ve diğer sorguların önüne geçer.
LIMIT ifadesinin temel sözdizimi şöyledir:
SELECT kolon1, kolon2 FROM tablo_adi LIMIT sayi;
Ya da iki parametre ile kullanımı:
SELECT kolon1, kolon2 FROM tablo_adi LIMIT baslangic, sayi;
Burada baslangic kaçıncı satırdan itibaren başlanacağını, sayi ise kaç satır getirileceğini belirtir. Dikkat edilmesi gereken nokta şudur: MySQL ve MariaDB’de satır indeksi 0’dan başlar. Yani ilk satır 0. indekstedir.
Temel Kullanım Örnekleri
Önce basit bir senaryo ile başlayalım. Diyelim ki bir e-ticaret uygulamanızın siparisler tablosu var ve son 10 siparişi görmek istiyorsunuz.
SELECT siparis_id, musteri_adi, toplam_tutar, siparis_tarihi
FROM siparisler
ORDER BY siparis_tarihi DESC
LIMIT 10;
Bu sorgu, tarihe göre azalan sırayla sıralanmış ilk 10 kaydı getirir. ORDER BY ile birlikte LIMIT kullanmak son derece önemlidir; aksi takdirde hangi 10 kaydın geleceği belirsiz olur ve her çalıştırıldığında farklı sonuçlar dönebilir.
Şimdi iki parametreli kullanıma bakalım. Diyelim ki ikinci sayfayı göstermek istiyorsunuz, her sayfada 10 kayıt var:
SELECT siparis_id, musteri_adi, toplam_tutar, siparis_tarihi
FROM siparisler
ORDER BY siparis_tarihi DESC
LIMIT 10, 10;
Burada 10, 10 ifadesi şu anlama gelir: 10. indeksten başla (yani 11. kayıt) ve 10 kayıt getir. Bu yapı, web uygulamalarında sayfalama için yaygın olarak kullanılır.
OFFSET ile Kullanım
MySQL 5.x ve MariaDB 10.x sürümlerinde LIMIT ile birlikte OFFSET anahtar kelimesini de kullanabilirsiniz. Bu yaklaşım kodu daha okunabilir kılar:
SELECT siparis_id, musteri_adi, toplam_tutar
FROM siparisler
ORDER BY siparis_id ASC
LIMIT 10 OFFSET 20;
Bu sorgu 21. kayıttan başlayarak 10 kayıt getirir. LIMIT 20, 10 ile tamamen aynı sonucu verir, ancak OFFSET kullanımı özellikle dinamik sorgularda daha anlaşılır bir yapı sunar.
Önemli not: LIMIT sayi OFFSET baslangic ile LIMIT baslangic, sayi sözdizimi aynı işi yapar ama parametrelerin sırası farklıdır. Bu iki sözdizimini karıştırmak çok yaygın bir hata kaynağıdır.
Gerçek Dünya Senaryosu: Log Tablosu Yönetimi
Bir sistem yöneticisi olarak büyük ihtimalle şöyle bir durumla karşılaşmışsınızdır: Uygulama geliştiricisi gelir ve “veritabanında bir şeylere bakabilir misin?” der. Siz de SELECT * FROM application_logs yazarsınız ve sunucu donmaya başlar. Bu senaryonun önüne geçmek için şu yaklaşımı benimseyin:
-- Önce tablonun boyutunu öğren
SELECT COUNT(*) FROM application_logs;
-- Sonra güvenli bir şekilde örnekleme yap
SELECT log_id, log_level, log_message, created_at
FROM application_logs
WHERE log_level = 'ERROR'
ORDER BY created_at DESC
LIMIT 50;
Bu yaklaşımla önce tablodaki toplam kayıt sayısını öğreniyorsunuz, ardından sadece hata seviyesindeki son 50 kaydı çekiyorsunuz. Hem sunucuyu koruyorsunuz hem de işinize yarayan veriyi elde ediyorsunuz.
WHERE ile Birlikte Kullanım
LIMIT, WHERE koşullarıyla birlikte kullanıldığında çok daha güçlü hale gelir. Şöyle düşünün: Bir müşteri destek sisteminiz var ve belirli bir müşterinin son 5 destek talebini görmek istiyorsunuz:
SELECT talep_id, konu, durum, olusturma_tarihi
FROM destek_talepleri
WHERE musteri_id = 1042
AND durum IN ('açık', 'beklemede')
ORDER BY olusturma_tarihi DESC
LIMIT 5;
Bu sorgu, 1042 ID’li müşterinin açık veya beklemedeki son 5 talebini getirir. Gereksiz veriyi daha sorgulama aşamasında süzüp LIMIT ile de sayıyı kısıtlıyorsunuz.
Sayfalama (Pagination) Implementasyonu
Web uygulamalarında sayfalama, LIMIT ve OFFSET kullanımının en yaygın senaryosudur. Bir blog uygulamasında her sayfada 20 makale göstereceğinizi varsayalım:
-- 1. sayfa (kayıtlar 1-20)
SELECT makale_id, baslik, yazar, yayinlanma_tarihi
FROM makaleler
WHERE durum = 'yayında'
ORDER BY yayinlanma_tarihi DESC
LIMIT 20 OFFSET 0;
-- 2. sayfa (kayıtlar 21-40)
SELECT makale_id, baslik, yazar, yayinlanma_tarihi
FROM makaleler
WHERE durum = 'yayında'
ORDER BY yayinlanma_tarihi DESC
LIMIT 20 OFFSET 20;
-- 3. sayfa (kayıtlar 41-60)
SELECT makale_id, baslik, yazar, yayinlanma_tarihi
FROM makaleler
WHERE durum = 'yayında'
ORDER BY yayinlanma_tarihi DESC
LIMIT 20 OFFSET 40;
Sayfa numarasından offset değerini hesaplamak için şu formülü kullanabilirsiniz: OFFSET = (sayfa_numarasi - 1) * sayfa_basina_kayit. Bu hesabı uygulama katmanında yapıp dinamik olarak sorguya ekliyorsunuz.
Büyük OFFSET değerlerinde performans sorunu: Çok büyük OFFSET değerlerinde MySQL ve MariaDB önce tüm satırları tarar, ardından istenmeyen kısmı atar. Örneğin LIMIT 10 OFFSET 500000 ifadesi, sunucunun 500.010 satırı okuyup 500.000’ini çöpe atması anlamına gelir. Bu durumla mücadele etmek için keyset pagination veya cursor-based pagination yöntemlerini tercih edin.
JOIN ile Birlikte LIMIT Kullanımı
LIMIT ifadesini JOIN sorgularında kullanırken dikkatli olmak gerekir. Şu senaryoyu ele alalım: Her kategorideki en pahalı ürünü bulmak istiyorsunuz.
SELECT u.urun_id, u.urun_adi, k.kategori_adi, u.fiyat
FROM urunler u
INNER JOIN kategoriler k ON u.kategori_id = k.kategori_id
ORDER BY u.fiyat DESC
LIMIT 10;
Bu sorgu, tüm kategorilerdeki en pahalı 10 ürünü getirir. Ancak “her kategoriden en pahalı 1 ürün” demek istiyorsanız bu sorgu istediğinizi vermez; o senaryo için subquery veya pencere fonksiyonları (window functions) kullanmanız gerekir.
Aşağıdaki örnek ise iki tabloyu birleştirip belirli bir satıcının son 3 siparişini getirir:
SELECT s.siparis_id, m.ad, m.soyad, s.toplam_tutar, s.siparis_tarihi
FROM siparisler s
INNER JOIN musteriler m ON s.musteri_id = m.musteri_id
WHERE s.satici_id = 7
ORDER BY s.siparis_tarihi DESC
LIMIT 3;
Subquery ile LIMIT Kullanımı
Zaman zaman bir alt sorgunun sonucunu kısıtlamanız gerekebilir. MySQL ve MariaDB’de bu durumda dikkat edilmesi gereken özel bir durum vardır: IN ve ALL gibi operatörlerde doğrudan subquery’e LIMIT uygulayamazsınız, ancak türetilmiş tablo (derived table) kullanarak bu kısıtı aşabilirsiniz.
-- En çok sipariş veren ilk 5 müşterinin detaylarını getir
SELECT m.musteri_id, m.ad, m.soyad, m.email
FROM musteriler m
WHERE m.musteri_id IN (
SELECT musteri_id
FROM (
SELECT musteri_id, COUNT(*) AS siparis_sayisi
FROM siparisler
GROUP BY musteri_id
ORDER BY siparis_sayisi DESC
LIMIT 5
) AS top_musteriler
);
Bu sorgu biraz karmaşık görünse de mantığı şudur: Önce en fazla sipariş veren 5 müşteriyi bulan bir alt sorgu yazıyorsunuz, bunu türetilmiş tablo haline getiriyorsunuz ve dış sorguda bu müşterilerin detaylarını çekiyorsunuz.
UPDATE ve DELETE ile LIMIT Kullanımı
LIMIT sadece SELECT sorgularına özgü değildir. UPDATE ve DELETE operasyonlarında da kullanılabilir ve üretim ortamında son derece değerlidir. Büyük tablolarda toplu güncelleme veya silme işlemi yaparken LIMIT kullanmak, tablo kilidini kısa tutmanızı ve sisteme aşırı yük bindirmemenizi sağlar.
-- Eski log kayıtlarını toplu olarak sil (batch silme)
DELETE FROM application_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 1000;
Bu yaklaşımı bir script içinde döngüyle kullanarak büyük silme işlemlerini küçük parçalara bölebilirsiniz:
-- Bash script içinde kullanım örneği
mysql -u root -p veritabani_adi -e "
DELETE FROM application_logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
LIMIT 1000;
SELECT ROW_COUNT() AS silinen_kayit;
"
Benzer şekilde UPDATE sorgularında da LIMIT kullanabilirsiniz:
-- Durum güncellemesini kontrollü şekilde yap
UPDATE urunler
SET stok_durumu = 'tükendi'
WHERE stok_miktari = 0
AND guncelleme_tarihi < DATE_SUB(NOW(), INTERVAL 7 DAY)
LIMIT 500;
Bu yaklaşım özellikle büyük tablolarda replikasyon gecikmesini minimize etmek için çok işe yarar. Bir seferde 500 satır güncellemek, 500.000 satırı tek sorguda güncellemekten çok daha güvenlidir.
LIMIT ile Performans Optimizasyonu
LIMIT kullanımı sorguları hızlandırabilir, ancak bunun garantisi yoktur. MySQL ve MariaDB sorgu optimizatörü, bazı durumlarda LIMIT varlığını fark edip farklı bir execution plan seçebilir.
EXPLAIN ile sorgu planını incelemek iyi bir alışkanlıktır:
EXPLAIN SELECT urun_id, urun_adi, fiyat
FROM urunler
ORDER BY fiyat DESC
LIMIT 10;
Bu komutun çıktısında Extra sütununda Using filesort veya Using temporary ifadelerini görürseniz, sorgunun optimize edilmesi gerekiyor olabilir. Özellikle ORDER BY ile birlikte kullanılan sütunlar üzerinde indeks oluşturmak, LIMIT sorgularının performansını dramatik biçimde artırır.
-- fiyat sütununa indeks ekle
CREATE INDEX idx_urun_fiyat ON urunler(fiyat);
-- Ardından sorguyu tekrar EXPLAIN ile incele
EXPLAIN SELECT urun_id, urun_adi, fiyat
FROM urunler
ORDER BY fiyat DESC
LIMIT 10;
İndeks eklendikten sonra Using index veya Using index condition ifadelerini görmeniz gerekir ki bu performansın iyileştiğinin göstergesidir.
Pratik Senaryo: Monitoring Sorguları
Bir DBA veya sysadmin olarak veritabanının sağlığını izlemek için düzenli olarak bazı sorgular çalıştırırsınız. Bu sorgularda da LIMIT hayat kurtarır:
-- En uzun süren son 10 sorguyu bul (slow query log tablosu varsa)
SELECT query_time, lock_time, rows_sent, sql_text
FROM mysql.slow_log
ORDER BY query_time DESC
LIMIT 10;
-- En büyük tabloları listele
SELECT table_schema, table_name,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS boyut_mb
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')
ORDER BY boyut_mb DESC
LIMIT 15;
Bu tür izleme sorgularında LIMIT olmadan çalışmak hem gereksiz I/O yaratır hem de terminalinize binlerce satır dökmesine neden olur.
Yaygın Hatalar ve Dikkat Edilmesi Gerekenler
- ORDER BY olmadan LIMIT kullanmak: Hangi satırların geleceği belirsizdir. Her çalıştırmada farklı sonuçlar alabilirsiniz.
- OFFSET değerini çok büyük tutmak:
LIMIT 10 OFFSET 1000000gibi sorgular ciddi performans sorunlarına yol açar. Keyset pagination alternatifini değerlendirin. - Yanlış parametre sırası:
LIMIT baslangic, sayiileLIMIT sayi OFFSET baslangicsözdizimlerini karıştırmak hatalı sonuçlara neden olur. - Subquery’de doğrudan LIMIT:
IN (SELECT ... LIMIT 5)sözdizimi bazı MySQL sürümlerinde hata verir. Türetilmiş tablo yaklaşımını kullanın. - COUNT ile LIMIT birlikte kullanmak:
SELECT COUNT() FROM tablo LIMIT 10ifadesindekiLIMITetkisizdir çünküCOUNT()tek bir değer döndürür.
Sonuç
LIMIT ifadesi, MySQL ve MariaDB’nin en sık kullanılan ve en değerli özelliklerinden biridir. Basit göründüğü halde doğru kullanımı hem uygulama performansını artırır hem de veritabanı sunucusunu gereksiz yükten korur. Özellikle üretim ortamında çalışan büyük tablolarda LIMIT kullanmayı bir refleks haline getirmek gerekir.
SELECT, UPDATE ve DELETE sorgularında LIMIT kullanmak; sayfalama implementasyonlarında OFFSET ile birleştirmek; performans sorunlarıyla karşılaşıldığında EXPLAIN ile sorgu planını incelemek ve gerektiğinde indeks eklemek, olgun bir veritabanı yönetiminin temel taşlarından birini oluşturur.
Büyük veri operasyonlarını küçük parçalara bölmek için DELETE ve UPDATE sorgularında LIMIT kullanma alışkanlığı kazanmak ise hem replikasyon sağlığını korur hem de tablo kilitlerinden kaynaklanan kesintileri minimize eder. Bu teknikleri günlük veritabanı yönetim pratiğinize entegre ettiğinizde, hem sistemleriniz daha stabil çalışır hem de olası performans sorunlarının önüne geçmiş olursunuz.
