MariaDB ve MySQL’de ORDER BY ile Sonuçları Sıralama

Veritabanı yönetiminde en çok kullanılan operasyonlardan biri sorgulama sonuçlarını anlamlı bir sıraya koymaktır. Günde yüzlerce sorgu yazan bir DBA olarak söyleyebilirim ki, ORDER BY kullanmadan yazılan sorgular çoğu zaman veri tabanının kafasına göre sıralanmış sonuçlar döndürür ve bu durum raporlama, uygulama geliştirme ve veri analizi süreçlerinde ciddi sorunlara yol açar. Bu yazıda MariaDB ve MySQL üzerinde ORDER BY kullanımını tüm detaylarıyla inceleyeceğiz.

ORDER BY Nedir ve Neden Kullanılır

SQL sorgularında ORDER BY ifadesi, sorgu sonuçlarını belirli bir sütuna veya sütunlara göre sıralamak için kullanılır. Varsayılan olarak MariaDB ve MySQL herhangi bir garanti vermez; yani ORDER BY kullanmadığınızda sonuçların hangi sırayla geleceği belirsizdir. Bu belirsizlik küçük projelerde sorun yaratmayabilir ama milyonlarca kayıt içeren production veritabanlarında hem performans hem de veri tutarlılığı açısından felakete davetiye çıkarır.

Örneğin bir e-ticaret sitesinin ürün listesini düşünün. Kullanıcıya “en yeni ürünler” sayfasını gösteriyorsunuz ama ORDER BY kullanmadınız. Veritabanı motorunun iç yapısına, index durumuna veya son yapılan işlemlere göre sonuçlar her seferinde farklı gelebilir. Bu hem kullanıcı deneyimini mahveder hem de uygulamanızda hata ayıklamayı neredeyse imkânsız hale getirir.

Temel Söz Dizimi

ORDER BY kullanımının temel yapısı şu şekildedir:

SELECT sutun1, sutun2, sutun3
FROM tablo_adi
WHERE kosul
ORDER BY sutun_adi [ASC | DESC];

Buradaki parametreler:

  • ASC: Artan sıralama (A’dan Z’ye, küçükten büyüğe). Varsayılan değerdir.
  • DESC: Azalan sıralama (Z’den A’ya, büyükten küçüğe).

Hadi gerçek bir örnek üzerinden gidelim. Önce test veritabanımızı oluşturalım:

CREATE DATABASE eticaret;
USE eticaret;

CREATE TABLE urunler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    urun_adi VARCHAR(100),
    kategori VARCHAR(50),
    fiyat DECIMAL(10,2),
    stok INT,
    olusturma_tarihi DATETIME DEFAULT NOW(),
    aktif TINYINT(1) DEFAULT 1
);

INSERT INTO urunler (urun_adi, kategori, fiyat, stok) VALUES
('Laptop Pro 15', 'Elektronik', 15999.99, 45),
('Mekanik Klavye', 'Elektronik', 899.50, 120),
('Ofis Sandalyesi', 'Mobilya', 2499.00, 30),
('USB Hub 7 Port', 'Elektronik', 349.90, 200),
('Ayarlanabilir Masa', 'Mobilya', 4999.00, 15),
('Webcam 4K', 'Elektronik', 1299.00, 75),
('Mouse Pad XL', 'Aksesuar', 199.90, 500),
('Monitör 27 inch', 'Elektronik', 8499.00, 60),
('Laptop Standı', 'Aksesuar', 649.00, 150),
('Kablosuz Mouse', 'Elektronik', 549.90, 95);

Tek Sütuna Göre Sıralama

En basit kullanım tek bir sütuna göre sıralama yapmaktır:

-- Fiyata gore artan siralamayla urunleri listele
SELECT urun_adi, kategori, fiyat
FROM urunler
ORDER BY fiyat ASC;

-- Fiyata gore azalan siralamayla en pahali urunden baslat
SELECT urun_adi, kategori, fiyat
FROM urunler
ORDER BY fiyat DESC;

-- Urun adina gore alfabetik siralama (ASC varsayilan oldugu icin yazilmasa da olur)
SELECT urun_adi, fiyat
FROM urunler
ORDER BY urun_adi;

Bu sorgulardan sonuncusunda ASC yazmadık çünkü varsayılan davranış zaten artan sıralamadır. Ancak ben her zaman açık yazmayı tercih ederim. Kodu okuyan başka bir DBA veya geliştirici için niyetin açık olması, “acaba bu ASC mi DESC mi olmalıydı” sorusunu ortadan kaldırır.

Birden Fazla Sütuna Göre Sıralama

Gerçek dünya senaryolarında genellikle birden fazla kritere göre sıralama yaparız. Bunu virgülle birden fazla sütun belirterek yapabiliriz:

-- Once kategoriye gore alfabetik, ayni kategoride fiyata gore azalan siralama
SELECT urun_adi, kategori, fiyat, stok
FROM urunler
ORDER BY kategori ASC, fiyat DESC;

Bu sorguda önce kategori sütununa göre A’dan Z’ye sıralama yapılır. Aynı kategorideki ürünler arasında ise fiyat sütununa göre pahalıdan ucuza doğru sıralama uygulanır.

Bunu müşteri sipariş takip sisteminde düşünelim. Siparişleri önce durumuna göre (bekliyor, onaylandı, kargoda, teslim edildi), aynı durumdakileri ise en yeni tarihten itibaren görmek istiyoruz:

CREATE TABLE siparisler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    musteri_adi VARCHAR(100),
    siparis_tarihi DATETIME DEFAULT NOW(),
    toplam_tutar DECIMAL(10,2),
    durum ENUM('bekliyor','onaylandi','kargoda','teslim_edildi','iptal')
);

INSERT INTO siparisler (musteri_adi, siparis_tarihi, toplam_tutar, durum) VALUES
('Ahmet Yilmaz', '2024-01-15 10:30:00', 1299.00, 'bekliyor'),
('Fatma Demir', '2024-01-14 14:20:00', 899.50, 'onaylandi'),
('Mehmet Kaya', '2024-01-13 09:15:00', 15999.99, 'kargoda'),
('Ayse Sahin', '2024-01-15 16:45:00', 2499.00, 'bekliyor'),
('Ali Can', '2024-01-12 11:30:00', 349.90, 'teslim_edildi'),
('Zeynep Ak', '2024-01-15 08:00:00', 4999.00, 'bekliyor');

-- Duruma gore, ayni durumda en yeni siparisler once gelsin
SELECT musteri_adi, siparis_tarihi, toplam_tutar, durum
FROM siparisler
ORDER BY durum ASC, siparis_tarihi DESC;

Sütun Pozisyonu ile Sıralama

ORDER BY içinde sütun adı yerine SELECT listesindeki sütunun pozisyon numarasını da kullanabilirsiniz. Bu özellikle uzun sütun adlarında veya alias kullanan sorgularda pratiktir:

-- SELECT listesindeki 3. sutun (fiyat) ve 2. sutuna (kategori) gore sirala
SELECT urun_adi, kategori, fiyat
FROM urunler
ORDER BY 2 ASC, 3 DESC;

Ancak burada bir uyarı: Pozisyon numarasıyla sıralama bakımı güç kodlara yol açar. SELECT listesine yeni bir sütun eklediğinizde tüm pozisyon numaraları kayar ve sorgularınız beklenmedik sonuçlar üretir. Bu yüzden production kodunda sütun adını açık yazmayı şiddetle tavsiye ederim. Pozisyon numarasını en fazla hızlı test sorgularında kullanıyorum.

Alias Kullanarak Sıralama

Hesaplanmış değerler veya uzun ifadeler söz konusu olduğunda alias (takma ad) kullanarak ORDER BY yazmak kodu hem okunabilir hem de bakımı kolay hale getirir:

-- Stok degeri hesaplanarak siralamayla
SELECT 
    urun_adi,
    fiyat,
    stok,
    (fiyat * stok) AS toplam_stok_degeri
FROM urunler
ORDER BY toplam_stok_degeri DESC;

Bu sorgu hangi ürünün stoğunun toplam parasal değerinin en yüksek olduğunu gösterir. Lojistik ve depo yönetimi açısından kritik bir rapordur.

WHERE ile Birlikte Kullanım

ORDER BY genellikle WHERE ile birlikte kullanılır. SQL’de sorgu çalışma sırası şöyledir: FROM -> WHERE -> SELECT -> ORDER BY. Yani önce filtreleme yapılır, sonra sıralama uygulanır:

-- Sadece Elektronik kategorisindeki urunleri fiyata gore sirala
SELECT urun_adi, fiyat, stok
FROM urunler
WHERE kategori = 'Elektronik' AND aktif = 1
ORDER BY fiyat DESC;

-- Stoku 100'den fazla olan urunleri stoga gore sirala
SELECT urun_adi, kategori, stok, fiyat
FROM urunler
WHERE stok > 100
ORDER BY stok ASC;

NULL Değerlerle Sıralama

MariaDB ve MySQL’de NULL değerler ORDER BY davranışı açısından farklıdır:

  • ASC sıralamada: NULL değerler en başa gelir (en küçük kabul edilir).
  • DESC sıralamada: NULL değerler en sona kalır.

Bu davranış bazı durumlarda istediğiniz gibi olmayabilir. Örneğin silinmiş veya güncellenmemiş kayıtların NULL tarihlere sahip olduğu bir durumda, bu kayıtların en sona gitmesini isteyebilirsiniz:

-- NULL degerlerle calisma ornegi icin tabloyu guncelle
ALTER TABLE urunler ADD COLUMN guncelleme_tarihi DATETIME NULL;

UPDATE urunler SET guncelleme_tarihi = '2024-01-10' WHERE id IN (1,3,5,7);

-- NULL degerleri en sona atmak icin ISNULL() veya IS NULL kullan
SELECT urun_adi, guncelleme_tarihi
FROM urunler
ORDER BY ISNULL(guncelleme_tarihi) ASC, guncelleme_tarihi DESC;

-- Alternatif olarak CASE WHEN ile NULL kontrolu
SELECT urun_adi, guncelleme_tarihi
FROM urunler
ORDER BY 
    CASE WHEN guncelleme_tarihi IS NULL THEN 1 ELSE 0 END ASC,
    guncelleme_tarihi DESC;

ISNULL() fonksiyonu NULL için 1, diğerleri için 0 döndürür. Böylece NULL kayıtlar en sona itilir ve geri kalanlar kendi içinde tarih sırasına göre dizilir.

CASE WHEN ile Özel Sıralama Mantığı

Bazen sütundaki değerlere göre özel bir sıralama mantığı uygulamanız gerekir. CASE WHEN ifadesi bu konuda çok güçlü bir araçtır:

-- Siparis durumlarini is akisina gore ozel siralamayla goster
-- bekliyor -> onaylandi -> kargoda -> teslim_edildi -> iptal
SELECT musteri_adi, toplam_tutar, durum, siparis_tarihi
FROM siparisler
ORDER BY 
    CASE durum
        WHEN 'bekliyor' THEN 1
        WHEN 'onaylandi' THEN 2
        WHEN 'kargoda' THEN 3
        WHEN 'teslim_edildi' THEN 4
        WHEN 'iptal' THEN 5
        ELSE 6
    END ASC,
    siparis_tarihi DESC;

Bu sorgu siparişleri iş akışına göre sıralar. Envanter yönetim sistemleri, helpdesk uygulamaları ve herhangi bir duruma göre işlem yapan sistemlerde bu tür özel sıralama mantığına sık sık başvururuz.

LIMIT ile Birlikte Kullanım

ORDER BY en çok LIMIT ile birlikte kullanılır. “En pahalı 5 ürün”, “son 10 sipariş” gibi sorgular bu kombinasyonla yazılır:

-- En pahali 5 urun
SELECT urun_adi, kategori, fiyat
FROM urunler
WHERE aktif = 1
ORDER BY fiyat DESC
LIMIT 5;

-- En son gelen 10 siparis
SELECT musteri_adi, siparis_tarihi, toplam_tutar, durum
FROM siparisler
ORDER BY siparis_tarihi DESC
LIMIT 10;

-- Sayfalama icin LIMIT ve OFFSET kombinasyonu (sayfa 2, her sayfada 5 kayit)
SELECT urun_adi, fiyat, kategori
FROM urunler
ORDER BY urun_adi ASC
LIMIT 5 OFFSET 5;

Sayfalama (pagination) işlemleri için ORDER BY olmadan LIMIT/OFFSET kullanmak tehlikelidir. Hangi kayıtların döneceği belirsizleşir ve kullanıcı aynı kaydı iki farklı sayfada görebilir ya da bazı kayıtlar hiç görünmeyebilir.

JOIN Sorgularında ORDER BY

Birden fazla tabloyu birleştirdiğinizde ORDER BY hangi tablonun hangi sütununu kullanacağını bilmek için tablo adını veya alias’ı belirtmeniz gerekebilir:

-- Kategoriler tablosu olustur
CREATE TABLE kategoriler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    kategori_adi VARCHAR(50),
    oncelik INT DEFAULT 0
);

INSERT INTO kategoriler (kategori_adi, oncelik) VALUES
('Elektronik', 1),
('Mobilya', 2),
('Aksesuar', 3);

-- JOIN ile kategori oncelik sirasina gore urunleri listele
SELECT 
    u.urun_adi,
    u.fiyat,
    u.stok,
    k.kategori_adi,
    k.oncelik
FROM urunler u
INNER JOIN kategoriler k ON u.kategori = k.kategori_adi
WHERE u.aktif = 1
ORDER BY k.oncelik ASC, u.fiyat DESC;

Bu sorguda k.oncelik ve u.fiyat yazarak hangi tablonun sütunundan bahsettiğimizi açıkça belirttik. Aynı isimde sütunlar olduğunda bu zorunlu hale gelir.

Performans Açısından ORDER BY

Bu noktada biraz sysadmin kafasıyla düşünelim. ORDER BY kullanmak performans maliyeti taşır. Büyük veri setlerinde sıralama işlemi hem CPU hem de bellek açısından ağırdır. Bazı kritik noktalar:

  • Index kullanımı: ORDER BY içinde kullandığınız sütunlarda index varsa MariaDB/MySQL bu index’i sıralama için kullanabilir ve dosya tabanlı sıralama (filesort) işleminden kaçınılır.
  • Filesort: Index’in kullanılamadığı durumlarda veritabanı motoru bellekte veya geçici disk dosyasında sıralama yapar. EXPLAIN çıktısında Using filesort görüyorsanız bu durum söz konusudur.
  • Index oluşturma: Sık kullanılan ORDER BY sütunlarına index ekleyin.
-- EXPLAIN ile sorgu planini incele
EXPLAIN SELECT urun_adi, fiyat
FROM urunler
ORDER BY fiyat DESCG

-- Fiyat sutununa index ekle
CREATE INDEX idx_urunler_fiyat ON urunler(fiyat);

-- Cok sutunlu index (kategori ve fiyata gore sorgular icin)
CREATE INDEX idx_urunler_kategori_fiyat ON urunler(kategori, fiyat);

-- Index sonrasi tekrar kontrol et
EXPLAIN SELECT urun_adi, fiyat
FROM urunler
WHERE kategori = 'Elektronik'
ORDER BY fiyat DESCG

EXPLAIN çıktısında Extra sütununda Using index veya boş görüyorsanız verimli çalışıyorsunuz demektir. Using filesort görüyorsanız index eklemeyi veya sorguyu yeniden yazmayı değerlendirin.

Gerçek Dünya Senaryosu: Log Tablosu Analizi

Production ortamında sıklıkla karşılaştığım bir senaryo: Uygulama log’larını analiz etmek. Yavaş sorguları veya hataları bulmak için ORDER BY hayat kurtarır:

-- Uygulama log tablosu
CREATE TABLE uygulama_loglari (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    log_zamani DATETIME DEFAULT NOW(),
    seviye ENUM('DEBUG','INFO','WARNING','ERROR','CRITICAL'),
    kaynak VARCHAR(100),
    mesaj TEXT,
    islem_suresi_ms INT
);

-- Onceki 24 saatin kritik ve error loglarini en yeni once
SELECT 
    log_zamani,
    seviye,
    kaynak,
    mesaj,
    islem_suresi_ms
FROM uygulama_loglari
WHERE 
    log_zamani >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
    AND seviye IN ('ERROR', 'CRITICAL')
ORDER BY 
    CASE seviye
        WHEN 'CRITICAL' THEN 1
        WHEN 'ERROR' THEN 2
        ELSE 3
    END ASC,
    log_zamani DESC
LIMIT 50;

-- En yavas islemleri bul (performans sorunlarini tespit)
SELECT 
    kaynak,
    COUNT(*) AS islem_sayisi,
    AVG(islem_suresi_ms) AS ortalama_sure,
    MAX(islem_suresi_ms) AS max_sure
FROM uygulama_loglari
WHERE log_zamani >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY kaynak
ORDER BY ortalama_sure DESC
LIMIT 10;

İkinci sorgu hangi uygulama bileşeninin en yavaş çalıştığını gösterir. Bu tür sorgular gece 3’te alarm geldiğinde saatler içinde sorunun kaynağını bulmanıza yardımcı olur.

GROUP BY ile ORDER BY Birlikte Kullanımı

GROUP BY ile ORDER BY birleşimi özellikle raporlama sorgularında çok kullanılır:

-- Kategoriye gore toplam satis degeri, en yuksekten en dusuge
SELECT 
    kategori,
    COUNT(*) AS urun_sayisi,
    AVG(fiyat) AS ortalama_fiyat,
    SUM(fiyat * stok) AS toplam_stok_degeri,
    MIN(fiyat) AS en_ucuz,
    MAX(fiyat) AS en_pahali
FROM urunler
WHERE aktif = 1
GROUP BY kategori
ORDER BY toplam_stok_degeri DESC;

ORDER BY içinde aggregate fonksiyon (SUM, AVG, COUNT vb.) veya bunların alias’larını kullanabilirsiniz. Bu özellikle haftalık/aylık raporlama sorgularında işe yarar.

Fonksiyonlarla Sıralama

ORDER BY içinde fonksiyon kullanmak mümkündür ancak dikkatli olmak gerekir çünkü fonksiyon kullanıldığında genellikle index devre dışı kalır:

-- Musteri adinin son soyadina gore sirala (pratik degil, ornek icin)
SELECT musteri_adi, toplam_tutar, siparis_tarihi
FROM siparisler
ORDER BY SUBSTRING_INDEX(musteri_adi, ' ', -1) ASC;

-- Tarihin ay bilgisine gore sirala
SELECT musteri_adi, siparis_tarihi, toplam_tutar
FROM siparisler
ORDER BY MONTH(siparis_tarihi) ASC, DAY(siparis_tarihi) ASC;

-- Rastgele siralama (A/B testi veya rastgele urun gosterimi icin)
SELECT urun_adi, fiyat, kategori
FROM urunler
WHERE aktif = 1
ORDER BY RAND()
LIMIT 5;

ORDER BY RAND() çok küçük tablolar dışında performans açısından felakettir. Büyük tablolarda tüm tabloyu tarar ve her satır için rastgele bir değer üretir. Production’da büyük tablolar için alternatif yöntemler kullanmanızı öneririm.

Karakter Seti ve Collation Etkisi

Türkçe karakter içeren veritabanlarında sıralama sorunlarıyla sık sık karşılaşılır. Örneğin ç, ş, ğ gibi karakterler Latin alfabesinden sonra gelebilir veya yanlış sıralanabilir:

-- Mevcut collation'i kontrol et
SHOW VARIABLES LIKE 'collation%';

-- Tablo seviyesinde Turkce uyumlu collation
CREATE TABLE musteriler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ad VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_turkish_ci;

-- Sorgu bazinda collation belirterek siralama
SELECT urun_adi
FROM urunler
ORDER BY urun_adi COLLATE utf8mb4_turkish_ci ASC;

utf8mb4_turkish_ci collation’ı Türk alfabesine göre doğru sıralama yapar. ci harfleri “case insensitive” anlamına gelir, yani büyük/küçük harf farkı gözetilmez. Production ortamında veritabanınızı kurarken character set ve collation seçimine dikkat etmek, ileride bu tür baş ağrılarını önler.

ORDER BY ile İlgili Yaygın Hatalar

Yıllar içinde gördüğüm en yaygın ORDER BY hatalarını paylaşayım:

  • GROUP BY olmadan aggregate fonksiyon sonucuna göre sıralama: COUNT(*) gibi fonksiyonları ORDER BY içinde kullanıyorsanız genellikle GROUP BY da gereklidir.
  • WHERE yerine HAVING kullanmak: ORDER BY ile birlikte filtreleme yaparken bazı geliştiriciler HAVING kullanır. HAVING aggregate sonuçları filtrelemek içindir; standart koşullar için WHERE kullanın, sorgunuz çok daha hızlı çalışır.
  • SELECT ile ORDER BY: SELECT kullanmak tüm sütunları çektiğinden gereksiz veri transferi yaratır. Sadece ihtiyacınız olan sütunları seçin.
  • Sıralama sütununun NULL durumunu görmezden gelmek: NULL değerler sıralamayı bozabilir. Önemli sütunlarda NULL kontrolü yapın.
-- Yanlis: HAVING ile normal filtre
SELECT urun_adi, fiyat
FROM urunler
GROUP BY urun_adi, fiyat
HAVING fiyat > 1000
ORDER BY fiyat DESC;

-- Dogru: WHERE ile filtre, sonra sirala
SELECT urun_adi, fiyat
FROM urunler
WHERE fiyat > 1000
ORDER BY fiyat DESC;

Sonuç

ORDER BY SQL’in en temel ama aynı zamanda en çok göz ardı edilen özelliklerinden biridir. Doğru kullanıldığında uygulamalarınızın hem güvenilirliğini hem de kullanıcı deneyimini ciddi şekilde artırır. Yanlış veya eksik kullanıldığında ise veri tutarsızlıklarına, performans sorunlarına ve üretimde sinir bozucu hatalara kapı açar.

Özetlemek gerekirse, dikkat etmeniz gereken temel noktalar:

  • Sonuç sırası önemli olan her sorguda mutlaka ORDER BY kullanın.
  • Birden fazla sütuna göre sıralama yaparken her sütun için ASC/DESC belirtin.
  • NULL değerlerin davranışını önceden anlayın ve gerekirse ISNULL() veya CASE WHEN ile kontrol altına alın.
  • Büyük tablolarda sık kullanılan ORDER BY sütunlarına index ekleyin.
  • EXPLAIN ile sorgu planınızı düzenli olarak kontrol edin ve Using filesort uyarılarını ciddiye alın.
  • Türkçe karakter içeren veriler için collation seçimine özen gösterin.
  • LIMIT/OFFSET ile sayfalama yaparken her zaman ORDER BY eşliğinde kullanın.

Veritabanı yönetimi tüm detaylarıyla öğrenilmesi yıllar alan bir uzmanlık alanıdır ama ORDER BY gibi temel konuları sağlam oturtmak, ileri seviye konuları da çok daha hızlı kavramanızı sağlar.

Bir yanıt yazın

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