Performans performans performans

Firebird ve Interbase veritabanları ve SQL komutlarıyla ilgli sorularınızı sorabilirsiniz. Delphi tarafındaki sorularınızı lütfen Programlama forumunda sorunuz.
Cevapla
aLonE CoDeR
Kıdemli Üye
Mesajlar: 1223
Kayıt: 26 Nis 2005 04:08

Performans performans performans

Mesaj gönderen aLonE CoDeR »

Selamlar.

Performansla alakalı bir çok konu konuşuldu forumda evet. Hafızam iyidir ama güvenmeyip tekrar araştırdım, okudum, uyguladım, analiz ettim vs.vs. Fakat ne yaptıysam çözüm olmadı. Hem de öyle ciddi bir kayıt rezervi de yok, aşağıda detaylandıracağım konuya gelecek yorumları merakla bekliyorum.

İşletim sistemi, makine konfigürasyonu olayına girmiyorum, Firebird sürümü 2.1.x (2.5'ta denemedim açıkçası). Hepi topu PK dahil 14 saha olan bir tablom var ve içeriği örneğin (sallıyorum) şöyle olsun;

Köylü Ahmet amcam, tarlaya çalışmaya giderken keyfine göre bazen traktörle bazen eşekle gidiyor. Ben de hem traktöre hem de eşeğe bir RFID destekli kart yerleştirdim gelip giderken kayıt tutuyorum. Köylü bunu beğendi başka isteyenler de oldu vs. Benden istedikleri de belli aralıklarda kaç gün eşekle kaç gün traktörle tarlaya gitmiş toplu raporla bize madem yazarım çizerim havasındasın diyorlar. RFID kartların tanımlı olduğu tablomuz da var ve bu tabloyla FK ilişkisi mevcut. Hareket tablosuna 135 bin kayıt girilmiş (sanırım çocuklar da oyun oynamış :P). Bu iş sağdan soldan duyulunca akın etmişler kabarmış köylü kaydı sayısı ve o da 25 bin civarı olmuş. İstediğim raporsa örneğin traktörü seçtiğimde bana en çok traktör kullananların listesini döksün diyor bu işin sorumlusu amcalar. Önce bir SP yazdım, for select dedim 25 bin köylü için bu analizi getirmesi oldukça zorladı. Hareket tablosundan distinct kullanarak bir kayıt listesi çıkardım o da tatmin edici değil. Gruplayarak aldığım sonuç esasen tatmin edici düzeyde fakat aynı satır üzerinde olması gereken bilgiler sonraki satırlarda gruplanarak detail olduğu için (karışık oldu galiba bu anlatım) gene istediğim sonucu alamıyorum. Belki de ben çok şey bekliyorum veritabanından, mesela bu işlemi bana 3-4 hadi 1 saniye de ben ekleyeyim 5 saniyede getirsin döksün önüme.. Siz sormadan ben söyleyeyim, where clause var ve o sahalar için gerekli indeksler de tanımlı. Hoş where clause olmasa da çok farketmiyor, amcaların sabrını zorluyor süre :)

Farklı bir üslupla anlattım ilk kez, umarım kusurum olmamıştır. Yorumlarınızı beklerim efendim :)
Kullanıcı avatarı
sabanakman
Kıdemli Üye
Mesajlar: 3081
Kayıt: 17 Nis 2006 08:11
Konum: Ah bi Antalya olaydı keşke (Ankara)

Re: Performans performans performans

Mesaj gönderen sabanakman »

FireBird olarak pek bir bilgim yok ama SQL Server üzerinden benzeri bir durumda gayet başarılı performans sağlayan sonucu aşağıdaki şekilde alt sorguya (subquery) ilişki (join) kurarak elde edilebilir.

Kod: Tümünü seç

select * from KOYLU as k
right join (select RFID as KOYLUID, sum(GUN) as TOPLAMGUN, sum(case when TUR='TRAKTOR' then GUN end) as TRAKTORLUGUN, sum(case when TUR='EŞEK' then GUN end) as ESEKLIGUN from HAREKET as h group by RFID) as h
left join h.KOYLUID=h.RFID
order by h.TOPLAMGUN desc
Şaban Şahin AKMAN
_________________
Derin olan kuyu değil kısa olan iptir. - .
aLonE CoDeR
Kıdemli Üye
Mesajlar: 1223
Kayıt: 26 Nis 2005 04:08

Re: Performans performans performans

Mesaj gönderen aLonE CoDeR »

İlgin için teşekkürler, syntax olarak Firebird'ün kullandığım sürümü bunu desteklemez diye düşünüyordum, yanıltmadı :) 2.5 te destekler mi, denemek gerek. Fakat farklı bir çıkış noktası keşfetmeme neden oldu yazdığın sorgu, performansı bir stored procedure içinde bazı kontrollerle besleyip istediğim kıvama getirmeye çalışacağım, kabul edilebilir bir sonuç alacağa benziyorum..

Tekrar teşekkürler.
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Re: Performans performans performans

Mesaj gönderen Lost Soul »

aLonE CoDeR yazdı:İlgin için teşekkürler, syntax olarak Firebird'ün kullandığım sürümü bunu desteklemez diye düşünüyordum, yanıltmadı :) 2.5 te destekler mi, denemek gerek. Fakat farklı bir çıkış noktası keşfetmeme neden oldu yazdığın sorgu, performansı bir stored procedure içinde bazı kontrollerle besleyip istediğim kıvama getirmeye çalışacağım, kabul edilebilir bir sonuç alacağa benziyorum..

Tekrar teşekkürler.
firebird 2.1 sürümünde verilen örnekteki yapı destekleniyor.
aLonE CoDeR
Kıdemli Üye
Mesajlar: 1223
Kayıt: 26 Nis 2005 04:08

Re: Performans performans performans

Mesaj gönderen aLonE CoDeR »

Lost Soul yazdı: firebird 2.1 sürümünde verilen örnekteki yapı destekleniyor.
Örnekler misin?
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Re: Performans performans performans

Mesaj gönderen Lost Soul »

ÖRNEK 1:LEFT JOIN VE CASE

Kod: Tümünü seç

        SELECT
        ANA.*,
        BILGI.STUR as BSTUR,
        BILGI.SMAT as BSMAT,
        BILGI.SFEN as BSFEN,
        BILGI.SSOS as BSSOS,
        BILGI.SDIL as BSDIL,
        BILGI.NTUR as BNTUR,
        BILGI.NMAT as BNMAT,
        BILGI.NFEN as BNFEN,
        BILGI.NSOS as BNSOS,
        BILGI.NDIL as BNDIL,
        BILGI.NTOP as BNTOP,
        BILGI.PUAN as BPUAN,
        BILGI.SINAVADI as SINAVADI,
        CEVAP.TUR as  CTUR,
        CEVAP.MAT as  CMAT,
        CEVAP.FEN as  CFEN,
        CEVAP.SOS as  CSOS,
        CEVAP.DIL as  CDIL,
        TBIL.ILADI as ILADI,
        TBILCE.ILCEADI as ILCEADI,
        TBKURUM.KURUMADI as KURUMADI,


        case coalesce(BILGI.STUR,0) when 0 then 0 else case when (((ANA.NTUR*100)/BILGI.STUR)<0) then 0 else ((ANA.NTUR*100)/BILGI.STUR) end end as BTUR,
        case coalesce(BILGI.SMAT,0) when 0 then 0 else case when (((ANA.NMAT*100)/BILGI.SMAT)<0) then 0 else ((ANA.NMAT*100)/BILGI.SMAT) end end as BMAT,
        case coalesce(BILGI.SFEN,0) when 0 then 0 else case when (((ANA.NFEN*100)/BILGI.SFEN)<0) then 0 else ((ANA.NFEN*100)/BILGI.SFEN) end end as BFEN,
        case coalesce(BILGI.SSOS,0) when 0 then 0 else case when (((ANA.NSOS*100)/BILGI.SSOS)<0) then 0 else ((ANA.NSOS*100)/BILGI.SSOS) end end as BSOS,
        case coalesce(BILGI.SDIL,0) when 0 then 0 else case when (((ANA.NDIL*100)/BILGI.SDIL)<0) then 0 else ((ANA.NDIL*100)/BILGI.SDIL) end end as BDIL,
        case (coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0)) when 0 then 0 else
         case when (((ANA.NTOP*100)/(coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0)))<0) then 0
         else ((ANA.NTOP*100)/(coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0))) end 
        end as BTOP,

        case coalesce(BILGI.STUR,0) when 0 then 0 else case when (((BILGI.NTUR*100)/BILGI.STUR)<0) then 0 else ((BILGI.NTUR*100)/BILGI.STUR) end end as BGOTUR,
        case coalesce(BILGI.SMAT,0) when 0 then 0 else case when (((BILGI.NMAT*100)/BILGI.SMAT)<0) then 0 else ((BILGI.NMAT*100)/BILGI.SMAT) end end as BGOMAT,
        case coalesce(BILGI.SFEN,0) when 0 then 0 else case when (((BILGI.NFEN*100)/BILGI.SFEN)<0) then 0 else ((BILGI.NFEN*100)/BILGI.SFEN) end end as BGOFEN,
        case coalesce(BILGI.SSOS,0) when 0 then 0 else case when (((BILGI.NSOS*100)/BILGI.SSOS)<0) then 0 else ((BILGI.NSOS*100)/BILGI.SSOS) end end as BGOSOS,
        case coalesce(BILGI.SDIL,0) when 0 then 0 else case when (((BILGI.NDIL*100)/BILGI.SDIL)<0) then 0 else ((BILGI.NDIL*100)/BILGI.SDIL) end end as BGODIL,
        case (coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0)) when 0 then 0 else
         case when (((BILGI.NTOP*100)/(coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0)))<0) then 0
         else ((BILGI.NTOP*100)/(coalesce(BILGI.STUR,0)+coalesce(BILGI.SMAT,0)+coalesce(BILGI.SFEN,0)+coalesce(BILGI.SSOS,0)+coalesce(BILGI.SDIL,0))) end 
        end as BGOTOP


        FROM OBGRSBS8 AS ANA

        left join SBSSINAV as BILGI on
        (ANA.SINAVTURU =BILGI.SINAVTURU and ANA.SINAVNO=BILGI.SINAVNO)

        left join SBSCEVANA as CEVAP on
        (ANA.SINAVTURU=CEVAP.SINAVTURU and ANA.SINAVNO=CEVAP.SINAVNO and CEVAP.KITAPCIK=ANA.KITAPCIK)

        left join ILLER as TBIL on
        (TBIL.ILKODU = ANA.ILKODU

        left join ILCELER as TBILCE on
        (TBILCE.ILCEKODU=ANA.ILCEKODU

        left join KURUM as TBKURUM on
        (TBKURUM.KURUMKODU=ANA.KURUMKODU)
        where ANA.SINAVTURU=:ilkSINAVTURU and ANA.SINAVNO=:ilkSINAVNO 

ÖRNEK 2: DINAMIK TABLO VE LEFT JOIN

Kod: Tümünü seç

SELECT ILADI FROM

(

    select iller.ilkodu,iller.iladi, ilceler.ilcekodu,ilceler.ilceadi
    from iller
    left join ilceler on ilceler.ilkodu = iller.ilkodu
)

 as
 DINAMIKTABLO

 WHERE DINAMIKTABLO.ILADI LIKE 'E%'
aLonE CoDeR
Kıdemli Üye
Mesajlar: 1223
Kayıt: 26 Nis 2005 04:08

Re: Performans performans performans

Mesaj gönderen aLonE CoDeR »

Yukardaki örnekte join için bir subquery kullanılmış ve desteklenmeyen noktası da burası. Senin verdiğin örnekte subquery join için değil select için kullanılmış. Benim gözümden mi kaçıyor bilmiyorum. İşin ilginç tarafı bahsettiğim gibi Şaban'ın verdiği örnekten yola çıkarak bir procedure yazdığımda 1 saniyede sonuç döndürdü :shock:
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Re: Performans performans performans

Mesaj gönderen Lost Soul »

aLonE CoDeR yazdı:Senin verdiğin örnekte subquery join için değil select için kullanılmış.
bahsettiğiniz join aşağıdaki gibi ise bu deyim de çalışıyor

Kod: Tümünü seç

SELECT ILADI FROM

(

    select iller.ilkodu,iller.iladi, DINAMIKILCE.ilcekodu,DINAMIKILCE.ilceadi
    from iller
    left join (SELECT * FROM ILCELER) AS DINAMIKILCE on DINAMIKILCE.ilkodu = iller.ilkodu
)

 as
 DINAMIKTABLO

 WHERE DINAMIKTABLO.ILADI LIKE 'E%'
burada ilçeler tablosunu join ederken dinamik tablo olarak çağırdım

ancak şöyle bir durum var. kesin olmamkla beraber bir tobloyu yukarddaki dinamik sorgu ile joinlemek o tabloyu tümüyle çekmek anlamına gelir ki bu da sorguya yük demek olur.

Kod: Tümünü seç

left join (SELECT * FROM ILCELER) AS DINAMIKILCE on DINAMIKILCE.ilkodu = iller.ilkodu
yerine

Kod: Tümünü seç

left join (SELECT * FROM ILCELER where  ILCELER.ILKODU =.......) AS DINAMIKILCE on DINAMIKILCE.ilkodu = iller.ilkodu
şeklinde kullanım sorgudaki yükü azaltabilir.


Dediğim gibi subquery olayı firebird 1.5 versiyonunda yok veya sınırlı idi ama 2.1 versiyonunda oldukça etkili bir şekilde kullanılabiliyor.
Kullanıcı avatarı
Lost Soul
Üye
Mesajlar: 1064
Kayıt: 01 Nis 2007 02:55
Konum: mekan ANKARA toprak ELAZIĞ
İletişim:

Re: Performans performans performans

Mesaj gönderen Lost Soul »

son olarak query'nin nerede yavaşladığını tespit etmek istiyorsanız ibexpert de f12 ile oluşturup çalıştırdığınız
query'nin durumunu plan analyzer ve performance analysis kısımlarında görebilirsiniz.

bkz.
Resim
Resim
aLonE CoDeR
Kıdemli Üye
Mesajlar: 1223
Kayıt: 26 Nis 2005 04:08

Re: Performans performans performans

Mesaj gönderen aLonE CoDeR »

İlginç, Şaban'ın verdiği yapıyı birebir uyguladığımda syntax hatası veriyor. İlgin için teşekkürler, problem kalmadı şu an.

Kolay gele.
Kullanıcı avatarı
freeman35
Admin
Mesajlar: 2380
Kayıt: 12 Haz 2003 04:05
Konum: merkez camii yanı

Re: Performans performans performans

Mesaj gönderen freeman35 »

index leri verirken sum vs kullanacağınız field larada verin,
coalesce yerine o alanları NOT null verin default value yada before triggerda bu alanlar nullsa 0 vs gibi değerler verin
yazılan her subquery HER SEFERİNDE çalıştırılır, buda işlemci yükü hafıza gerekliliği(bu mikisoft yüzünden daha fazla disk erişimi ve performans kaybı) ayrıca bilgileri diskten okuma gibi performans kaybı oluşturur. herbiri az olabilir ama toplanınca yekün yapar.
senin köylü emmiler traktör eşek gibi çok maksatlı götürgeçleri limitliyse sonrada yok ATV le gidicem yok ben jetskim le gidicem demezler se :N) ad soy ad table ına fazladan field ekleyip (traktörle gittiği adet, eşşekle gittiği adet gibi) detayları yazdığın table a trgigger la bu alanlar sum vs eklersen adını yanında herzaman sum lar çıkar.sadece kayıt sırasında sum sonuçları beklenirki bu saten göze batmaz fazla. Çünkü insalarda hep "bi tuşa basayım şaka diye tüm sayılar gelsin" denir :D yurdum insanı beklemeyi sevmez :) belki adetlerin haricinde değerler varsa bunlar 3.bi table dada tutulabilinir.
Bu mantığın şöle bir dez avantaşı var, kullanıcı ben iki tarih arası rapor istiyorum derse bu doğru çalışmaz :) mecburen iki table olacak yani subquery olacak, join bile olsa. join e yazdığınız query den sadece o kayıt geliyor ama o field ı bulmak için arka planda gene bir select oluşturuluyor unutmayın, bir milyon kayıt içinden bir kaydı nasıl bulabilir başka türlü.
illaki çok performans isteniyorsa donanım :) tabi SQL kurgusunun üstüne
Kolay gele
ZAGOR TENAY TÜRK'tür... TÜRK kalacak...
Zoru başarırım, İmkansız zaman alır
FreeMan 35.5

Soru sormaya üşenmiyorsan, sorunun çözümünü yazmaya da üşenme !!!
Kullanıcı avatarı
Kuri_YJ
Moderator
Mesajlar: 2248
Kayıt: 06 Ağu 2003 12:07
Konum: İstanbul
İletişim:

Re: Performans performans performans

Mesaj gönderen Kuri_YJ »

Selamlar,

Performans sıkıntısı yaşayan arkadaşlara bir tavsiye.

Eğer bir sorgunuz, bir başka sorgudan dönen sonuçlar üzerinde iş yapacaksa (nested queryler) hele ki büyük verilerle uğraşıyorsanız o zaman kodlamanızı biraz basitleştirin.

Yani

Kod: Tümünü seç

  SELECT T.*, NT.* 
  FROM TABLO1 T
    LEFT OUTER JOIN  (SELECT F1, SUM(F2), SUM(CASE WHEN SF3=1 THEN 10 ELSE 0 END) FROM TABLO2 WHERE F5=2) NT ON   T.SF99 = NT.F99
Yukarıda basitçe bir örnek yazmaya çalıştım şimdi bu hem memoryden hem performanstan yer. (Dikkat her durumda geçerli değildir)

Şimdi bu işi 2 adımda yaparsak hem belleği, hem cpuyu zorlamdan basitleştirebiliriz.

Kod: Tümünü seç

  INSERT INTO GECICI_TABLO 
  SELECT F1, SUM(F2), SUM(CASE WHEN SF3=1 THEN 10 ELSE 0 END) FROM TABLO2 WHERE F5=2) NT 
  /*SUM EDİLMİŞ BİR ÖZET GEÇİCİ BİR TABLOMUZU OLUŞTURDUK */
  /* ŞİMDİ BASİT BİR JOIN İLE TABLODAN İSTEDİĞİMİZİ ÇEKECEĞİZ */
  SELECT T.*, G.* 
  FROM TABLO1 T
    LEFT OUTER JOIN  GECICI_TABLO G ON T.SF99 = G.F99
Umarım anlatabilmişimdir. Yani diyeceğim şu, bir sürü sumlar coalesceler filan oluşturduğunnuz bir query'i bir başka query'ye source yapacaksanız, bunu geçici bir tabloya alın. Yani işlem adımlarınızı Serverı zorlamayacak şekilde basitleştirip adım adım yapın.

İlk başta işlem adımlarını uzattığını düşünebilirsiniz ama, karmaşık queryler hem sizi yorar hem makinayı ;) Olabildiğince basit queryler yazın ki server o basit queryleri çok daha kolay ve zorlanmadan (yorumlama zamanları, doğru indexi seçememe, çok bellek yeme gibi) zilyon tane dertten kurtarırsınız. Minimal queryler yazın, olsun bir adım fazla olsun, ama o bir adım fazlalık size çoook büyük veri hacimlerinde o kadar hızlandırır ki...

Yukarıdaki Query'i (Alone Coder arkadaşımızın soruları) bence 2 adımda query çeksin olay nasıl değişecek şaşıracaksınız. Bir deneyin...

Sene 2001-2002 civarıydı, o zamanlar 2 milyon kayıtlı bir database üzerinde ortalama aylık 20,000 civarı muhasebe fişinin girildiği bir uygulama yazmıştım ve FSB52 hesaplamaları ile Muhasebe yapılıyordu (Maliyet Muhasebesi).

Ben bazı işlemleri (FASB52 Hesaplarında) önce bir takım verileri SUM eder bir yere alırdım, ardından aldığım bu yerden bir başka kriterlere göre sumlayıp bir başka tabloya alırdım, ardından son selectimi çekerdim.

Daha önceleri bunu tek Query'de bitireyim diye çooook uğraştım. Ama verim çoook kötüydü, hem makina zorlanıyor hem bellek tükeniyor vs. vs.... Olayı basitleştirince, hem makina zorlanması ortadan kalktı hem benim query içerisinde değişiklik yapmam gerektiğinde, benim kodu okuyarak allem kullem yapmam basitleşti.

Olabilidiğince basit kodlar yazın, basit queryler yazın ama bütüne geldiğinizde karmaşık bir yapıyı ortaya koyabilmelisiniz.

LEGO'yu aklınızdan çıkarmayın, adamler LEGO parçalarıyla şehir kurdular ;) Basit parçalar ama bütüne varınca muhteşem şeyler çıkmakta !!!!!!!

İş yine biraz mimaride bitiyor, o sebeple DB'nizi hazırlarken de olabildiğince basitleştirilmiş bir DB ve basitleştrilmiş Indexler, basitleştirilmiş Query'ler...

Karmaşık Query yazmak, size ve makinaya yapılmış Zulümden başka bir şey değildir. Makina işletirken, siz Debug ederken veya değişiklik yaparken zorlanırsınız.

Baktınız olmuyor, yaaahuuuu yaz bir SP, 1 adımda tek query ile çekme, döndür 2 query yaz, 3 query yaz.. Ama basit olsun ;) Managementını kolaylaştırın.

Kimse sizi karmaşık query yazdınız diye alkışlamaz, tam tersine "Salak dalmış gitmiş boğulmuş derler ;)"

Amaaaan neyse benden bu kadar, daha yazacak var ama yeter bu kadar, sıkıldım :)

Haydi Kolay Gele.....


Not : Yukarıda söylediklerim her zaman geçerli şeyler değildir, genel olarak bahsettim, bazı projeye özel ya da duruma özel koşullarda 1000 satırlık Selectler yazabilirsiniz, bu tamamen duruma göre değişir. Ancak çoook genel kullanımda bu karmaşaya girmeyin diye söyledim.
Kuri Yalnız Jedi
Harbi Özgürlük İçin Pisi http://www.pisilinux.org/
Cevapla