Şimdilik böyle bilimsel makaleler yazabilecek biri değilim. Ama ileride belki birilerinin işine yarama ihtimalini düşünecek olursak Internet’te hep yiyen olmaktansa biraz da üretmek gerek ;) Biraz da Türkçe kaynaklarda bu sorunu kimsenin dile getirmediğini farkettim.

Sorun: Öncelikle problem herhangi bir veritabanı sisteminin çok sayıda kaydı veritabanından çekerken kayıtların rastgele dizilmesi için ORDER clause içinde kullanılan SQL RANDOM komutunun doğurduğu performans problemleri.

Açıklama: SQL’de kullanılan ORDER tümceciğinin içinde bir sıralama fonksiyonu olarak geçen RAND (veya RANDOM) çeşitli SQL yapılarında şu şekilde kullanılıyor:

SELECT column FROM table  ORDER BY RAND() LIMIT 1 // MYSQL


SELECT column FROM table ORDER BY RANDOM() LIMIT 1 // POSTGRESQL


SELECT TOP 1 column FROM table ORDER BY NEWID() // MICROSOFT SQL SERVER

Bu şekilde veritabanından alınacak veri için tüm tablodaki veriler LIMIT veya TOP gibi komutlara bakılmadan SQL tarafından teker teker sıraya diziliyor ve seçilen column (sütun) değerleri ve kayıtların rastgele dizilmiş yeni sıraları, mevcut bağlantı kapatılmadığı sürece hafızada tutuluyor. Fakat bu işlem, çok sayıda kayıta sahip tablolardan (en az 80.000 gibi bir sayıda elle tutulur sonuçlar elde ediliyor) yapılan rastgele kayıt alma sorgularında büyük performans-hız problemlerine ve sunucuda büyük bir yüke neden oluyor. Bunun nedeni ise öncelikle LIMIT komutunun değil, RANDOM ifade içeren ORDER clause’un çalıştırılması. Mantıklı olan da bu aslında, yani SQL doğru olanı yapıyor. Diğer türlü LIMIT-TOP ifadelerine 10 değerini verip sonra RANDOM komutunun çalışmasını beklersek ancak id veya kayıt sırası gibi değerlere göre ilk 10 kaydı çekip bunları sıralama yapmasını beklememiz gerekirdi.

Alternatif Çözümler : Dediğim gibi, on binlerce kayıt arasında işlem yapmıyorsanız, öncelikle bu probleme takılmadan yukarıdaki ORDER by RANDOM ifadesini rahatlıkla kullanabilirsiniz. Fakat alacağınız tek bir satır olsa bile, on binlerce kayıt barındıran bir tablodan bu komutla kayıt çekmek performans yönünden akıllıca değil. Buna çözüm olarak gösterebilecek en mantıklı çözüm, RANDOM komutunu doğrudan bir column gibi SELECT statement içine yerleştirmek. Aşağıdaki üç sorgunun benchmark sonuçları sanırım durumu anlatıyor.

  • Sorgu 1. SELECT … ORDER BY RAND()

  • Sorgu 2. SELECT RAND() * MAX(ID) …

  • Sorgu 3. SELECT RAND() * MAX(ID) + ORDER BY ID …


    Kayıt* || 100 || 1.000 || 10.000 || 100.000 || 1.000.000

    Sorgu 1|| 0:00.718s || 0:02.092s || 0:18.684s || 2:59.081s || 58:20.000s Sorgu 2|| 0:00.519s || 0:00.607s || 0:00.614s || 0:00.628s || 0:00.637s Sorgu 3|| 0:00.570s || 0:00.607s || 0:00.614s || 0:00.628s || 0:00.637s *Her sorgu 1000 defa çalıştırılmıştır Görüldüğü gibi _RANDOM _komutunun, _ORDER _tümcesi içine yerleştirilmesi, 1 milyon kayıtta 0.6 s. yerine 58.2 saniye gibi inanılmaz bir performans kaybı ve gereksiz yüke neden oluyor.

Bunun yanında ID kesintisi yaşamayacak olan sistemlerden son ID yi alıp ona kadar olan sayılardan N tane üretilip ‘where id=3 or id=5..’ gibi şekillerde de sonuçlar alınabilir. Fakat bu, kayıtların silinebildiği sistemlerde sağlıklı çalışmayabilir, eksik sayıda sonuç döndürebilir.

Sonuç: SQL RANDOM fonksiyonunu, büyük sayılarda kayıt içeren tablolarda ORDER cümleciği içince kullanmak, büyük performans problemlerine neden oluyor. Bu nedenle RANDOM fonskiyonu, doğrudan SELECT statement içinde kullanılmalıdır.