Aynı LOOP AT, üç farklı performans sınıfı
TROIA'da bir tablo üzerinde gezinmenin en az üç farklı yolu var, ve aralarındaki fark "biraz daha hızlı" seviyesinde değil — büyük tablolarda saniyelerle dakikalar arasında fark yaratabiliyor. Bu üç yolu (basit LOOP AT, CRITERIA COLUMNS, hash index'li arama) karşılaştırmadan önce şunu netleştirmek gerekiyor: TROIA'nın kendi yorumlayıcısı (interpreter) ile satır satır gezinmek, mümkün olduğunda işi Java katmanına devretmekten her zaman daha yavaştır. Belgelerin kendi ifadesiyle performans tavsiyesi net: "mümkün olduğunca Java katmanını kullanın."
Yol 1: Basit LOOP AT
LOOP AT {tablo} [WHERE {kosul}]
BEGIN
{blok}
ENDLOOP;
WHERE koşulu verilse bile, bu mod aslında tablonun her satırını tek tek TROIA yorumlayıcısında değerlendirir — koşula uymayanları da "gezer", sadece bloğu çalıştırmaz. Küçük tablolarda (birkaç yüz satır) bu fark hissedilmez; on binlerce satırlı bir tabloda gözle görülür bir yavaşlığa dönüşür.
Yol 2: CRITERIA COLUMNS
LOOP AT {tablo} CRITERIA COLUMNS {kolonlar} VALUES {degerler}
BEGIN
{blok}
ENDLOOP;
Bu mod, filtrelemeyi TROIA yorumlayıcısı yerine Java katmanına devrediyor — yani "her satırı gez, koşulu sen değerlendir" yerine "bana zaten filtrelenmiş satırları ver" demiş oluyorsunuz. Tek seferlik bir filtreleme için basit LOOP AT WHERE'den belirgin şekilde daha hızlıdır.
Yol 3: Hash index ile arama (en hızlısı, tekrarlı sorgularda)
BUILDHASHINDEX {indeksadi} COLUMNS {kolonlar} ON {tablo};
LOOP AT {tablo} CRITERIA INDEXED INDEX {indeksadi} VALUES {degerler}
BEGIN
{blok}
ENDLOOP;
Hash index'in maliyeti bir kereliktir (BUILDHASHINDEX çağrısı, tablonun büyüklüğüyle orantılı bir süre alır); ama index kurulduktan sonra aynı tablo üzerinde tekrar tekrar farklı değerlerle arama yapacaksanız, her aramanın maliyeti sabit zamana yakın hale gelir. Yani hash index, "bir kez ara" senaryosunda değil, "aynı tabloda yüzlerce kez farklı anahtarla ara" senaryosunda (örneğin bir sipariş listesinin her satırı için ilgili müşteri kaydını aynı müşteri tablosunda aramak) gerçek farkı yaratıyor.
Gerçek bir örnek: N sipariş için N müşteri araması
/* Kötü: her sipariş için tüm müşteri tablosunu WHERE ile tarar. */
LOOP AT SIPARISLER
BEGIN
LOOP AT MUSTERILER WHERE ID == SIPARISLER_MUSTERIID
BEGIN
SIPARISLER_MUSTERIADI = MUSTERILER_ADI;
BREAK;
ENDLOOP;
ENDLOOP;
/* İyi: müşteri tablosuna bir kez hash index kurup her sipariş için O(1)'e yakın arama yapar. */
BUILDHASHINDEX IDXMUSTERI COLUMNS 'ID' ON MUSTERILER;
LOOP AT SIPARISLER
BEGIN
LOOP AT MUSTERILER CRITERIA INDEXED INDEX IDXMUSTERI VALUES SIPARISLER_MUSTERIID
BEGIN
SIPARISLER_MUSTERIADI = MUSTERILER_ADI;
ENDLOOP;
ENDLOOP;
İlk yaklaşımda maliyet sipariş sayısı × müşteri sayısı ile orantılı büyür (N×M); ikinci yaklaşımda müşteri tablosu için index kurma maliyeti bir kez ödenir, sonrasında her arama sabit zamana yakındır.
Sık yapılan hatalar
- Her döngü iterasyonunda hash index'i yeniden kurmak —
BUILDHASHINDEX'i döngünün içine koymak, tam da kaçınmaya çalıştığınız maliyeti geri getirir. İndeks bir kez, dış döngünün öncesinde kurulmalıdır. - Tablo değiştiği halde eski index'i kullanmaya devam etmek — index kurulduktan sonra tabloya yeni satır eklerseniz, index bu yeni satırları yansıtmayabilir; tablo değiştiyse index'i yeniden kurmanız gerekir.
- Küçük tablolar için gereksiz index kurmak — birkaç düzine satırlık bir tabloda hash index'in kurulum maliyeti, kazandığı aramadan daha pahalı olabilir; bu optimizasyon gerçekten büyük ve tekrar sorgulanan tablolar için anlamlıdır.
WHEREkoşulunun index kullandığını sanmak — sadece koşul yazmak (LOOP AT ... WHERE) hiçbir zaman index kullanmaz, her zaman tam tarama yapar; hızlı filtreleme için mutlakaCRITERIA COLUMNSya daCRITERIA INDEXEDsözdizimine geçmeniz gerekir.
Sıradaki adım
Tek bir kayıt ararken (döngü kurmadan) kullanılan LOCATERECORD komutunu ve sıralı/ikili/index'li arama modlarını bir sonraki yazıda ele alacağım.
Yorumlar (0)
Yorum Yaz