İç içe sorgu yavaşlığı (ÇÖZÜLDÜ)

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

İç içe sorgu yavaşlığı (ÇÖZÜLDÜ)

Mesaj gönderen baloglurecep »

Arkadaşlar herkese sağlıklı günler dilerim. Yazdığım bir projedeki sorgu yavaşlığını bulmaya çalışıyorum. İki tablo içinden veri alarak sorgulama yapıyorum. iç içe sorgu kullanıyorum. yapmak istediğim ödeme yapmayan müşterileri bulmak. Sorgu çalışıyor ama program yanıt vermez hale geliyor donuyor. Aynı sorguyu ibexpert ile yapıyorum orda da aynı... sorgu çok geç cevap veriyor. ortalama 30sn. Gelen listeye dokunduğumda ibexpert donuyor. Müşteriler tablosunda 5000 civarı kayıt var. Satışlar tablosunda ise 150 bin civarı... Kodda bir anormallik olsa sorgulama yapmaz... Bu sorguyu nasıl farklılaştırabilirim nasıl hızlandırabilirim?

Kod: Tümünü seç

SELECT musteriler.adi_soyadi,musteriler.adres,musteriler.telefon,musteriler.telefon1,
((musteriler.satis+musteriler.taksitlisatis) - (musteriler.odemeler - musteriler.odemeler_taksit)) AS BAKIYE
FROM musteriler WHERE (Gizle=0) AND musterile[b][/b]r.Id not In (SELECT satislar.musteri_Id FROM satislar WHERE (satislar.tur_id=2))
En son baloglurecep tarafından 16 Nis 2020 01:55 tarihinde düzenlendi, toplamda 1 kere düzenlendi.
Kullanıcı avatarı
mussimsek
Admin
Mesajlar: 7587
Kayıt: 10 Haz 2003 12:26
Konum: İstanbul
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen mussimsek »

SubQuery'ler çok zorda kalmadıkça kullanılacak bir yöntem değil.

SQL cümlesini değiştirin.

Kod: Tümünü seç

SELECT M.adi_soyadi, M.adres, M.telefon, M.telefon1, ((M.satis+M.taksitlisatis) - (M.odemeler - M.odemeler_taksit)) AS BAKIYE
FROM musteriler as M
left outer join satislar as S on S.musteri_Id=M.Id
WHERE (M.Gizle=0) AND (S.tur_id=2)

Kolay gelsin.
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen baloglurecep »

Mussimsek hocam yanıtınız için öncelikle teşekkür ederim. Yalnız daha önce de innerjoin ile denemiştim sonuç alamamıştım. ibexpert sql kısmında kodu denediğimde de ibexpert donuyor...
Kullanıcı avatarı
mussimsek
Admin
Mesajlar: 7587
Kayıt: 10 Haz 2003 12:26
Konum: İstanbul
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen mussimsek »

Hocam 150bin kayıt çok fazla değil. Muhtemelen tablo dizaynınızda veya sisteminizde bir sıkıntı var o zaman.

Musteriler tablosunda Id alanı Primary key'mi?
Satislar tablosunda Musteri_Id alanı foreign key mi?

Değilse, veritabanınızın sağlam bir yedeğini alıp, bunları yapın ve tekrar bir deneyin.

Kolay gelsin.
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen baloglurecep »

Mussimsek hocam iki tabloda da otomatik artan id ler var. Müşteri tablosundaki id satışlar tablosunda muteri_id olarak kayıtlı. Yedek alıp yükledim. ama sorun yine devam ediyor.
ertank
Kıdemli Üye
Mesajlar: 1653
Kayıt: 12 Eyl 2015 12:45

Re: İç içe sorgu yavaşlığı

Mesaj gönderen ertank »

baloglurecep yazdı: 12 Nis 2020 04:37 Bu sorguyu nasıl farklılaştırabilirim nasıl hızlandırabilirim?
Merhaba,

Yaklaşık ifadeler yanılmaya sebebiyet verebilir. IBExpert içinde sorgu çalıştırıldıktan sonra History kısmında çalışma süresini milisaniye cinsinden bulabilirsiniz. Uygulamanızda da kod ile süre tutup bu bilgiyi elde edebilirsiniz.

Standart IBExpert kurulumu sorgu sonuçlarının tamamını getirmez. Ekrana sığacak kayıt sayısı kadarını getirir (yaklaşık 25-30 adet) siz ekranada gözükmeyen kayıtları görmek için aşağı inmek istediğinizde kalan kayıtları almak için sunucu ile iletişim kurar. Bu noktada sizin sorgunuz yavaş çalıştığı için "donma" etkisi gösteriyor olabilir. Bunu önlemek için Options -> Environment Options içinde SQL Editor kısmında Fetch All isimli kutuyu işaretlemeniz gerekir.

FirebirdSQL tarafında yapabileceklerinizden bazıları;
(1) "musteriler.Id not In (SELECT satislar.musteri_Id FROM satislar WHERE (satislar.tur_id=2))" ifadesini aşağıdaki örneğe benzer LEFT JOIN kullanacak şekilde değiştirmeyi deneyin. Bu tür kullanım şekli FirebirdSQL optimize edicisinin kafasını karıştırabilir. Kullanabileceği indeks olsa bile kullanmıyor olabilir. Mesela bu ifadeyi hiç kullanmadığınızda performans artıyor ise sorununuzun kaynağı bu ifadede yatıyordur.
(2) WHERE kelimesinde kullanılan kolonlar için indeks oluşturun (MUSTERILER tablosu: "Gizle" kolonu, "Id" kolonu, SATISLAR tablosu: "tur_id" kolonu). Ancak genel anlamda çok fazla indeks oluşturmaktan da kaçının.
(3) Kullandığınız FirebirdSQL sunucu sürümüne uygun optimize edilmiş ayar dosyalarını kullanın: https://ib-aid.com/en/optimized-firebird-configuration/

Yukarıdaki (1) numaralı öneri ile ilgili olarak:
Yaşadığınız yavaşlık sorununun bu madde olma ihtimali yüksek olduğuna inanıyorum. Bu anlamda LEFT JOIN kullanan örnek SQL:

Kod: Tümünü seç

SELECT 
  satislar.musteri_Id,
  musteriler.adi_soyadi,
  musteriler.adres,
  musteriler.telefon,
  musteriler.telefon1,
  SUM(((musteriler.satis+musteriler.taksitlisatis) - (musteriler.odemeler - musteriler.odemeler_taksit))) AS BAKIYE
FROM
  satislar
  LEFT JOIN MUSTERILER on musteriler.id = satislar.musteri_Id
WHERE 
 musteriler.Gizle=0 AND satislar.tur_id=2
GROUP BY 1,2,3,4,5
Örnek SQL için çalıştırma planı:

Kod: Tümünü seç

Plan
PLAN SORT (JOIN (SATISLAR NATURAL, MUSTERILER NATURAL))
Sizin kullandığınız sorgu için çalıştırma planı:

Kod: Tümünü seç

Plan
PLAN (SATISLAR NATURAL)
PLAN (SATISLAR NATURAL)
PLAN (MUSTERILER NATURAL)
Göreceğiniz üzere sizin kullandığınız sorgu satışlar tablosunu iki kere baştan sona okuyor. Tavsiye olarak verilen SQL ise her iki tabloyu tek seferde okuyor. Dolu veritabanı olmadığı için her iki sorgu arasındaki performans farkını ve her iki sorgu aynı sonucu veriyor mu kontrol etmek mümkün olmadı.

NOT: Örnek planlar FirebirdSQL 3.0.4 ile hazırlanmıştır. Farklı sürümlerde farklı sonuçlar alınabilir.
NOT2: Plan için kullanılan tablo yapıları aşağıda verilmiştir.

Kod: Tümünü seç

/* HIC INDEKS OLUSTURULMADI */
CREATE TABLE MUSTERILER (
    ID               INTEGER,
    ADI_SOYADI       VARCHAR(80),
    ADRES            VARCHAR(200),
    TELEFON          VARCHAR(20),
    TELEFON1         VARCHAR(20),
    SATIS            DECIMAL(18,4),
    TAKSITLISATIS    DECIMAL(18,4),
    ODEMELER         DECIMAL(18,4),
    ODEMELER_TAKSIT  DECIMAL(18,4),
    GIZLE            INTEGER
);


CREATE GENERATOR GEN_SATISLAR_ID;

/* SADECE AUTO INCREMENT KOLON ICIN INDEKS KULLANILDI */
CREATE TABLE SATISLAR (
    AUTOINC     INTEGER NOT NULL PRIMARY KEY,
    MUSTERI_ID  INTEGER,
    TUR_ID      INTEGER
);

SET TERM ^ ;

/* Trigger: SATISLAR_BI */
CREATE OR ALTER TRIGGER SATISLAR_BI FOR SATISLAR
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
  IF (NEW.AUTOINC IS NULL) THEN
    NEW.AUTOINC = GEN_ID(GEN_SATISLAR_ID,1);
END
^

SET TERM ; ^
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen baloglurecep »

Değerli arkadaşlar sql kodlamamı left hoin ile değiştirip bazı alanlara yeni indexler ekleyerek hızda gözle görülür bir değişim oldu. Tüm yanıt veren arkadaşlara teşekkürler ediyorum.
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

Re: İç içe sorgu yavaşlığı

Mesaj gönderen baloglurecep »

ertank yazdı: 13 Nis 2020 11:44
baloglurecep yazdı: 12 Nis 2020 04:37 Bu sorguyu nasıl farklılaştırabilirim nasıl hızlandırabilirim?
Merhaba,

Yaklaşık ifadeler yanılmaya sebebiyet verebilir. IBExpert içinde sorgu çalıştırıldıktan sonra History kısmında çalışma süresini milisaniye cinsinden bulabilirsiniz. Uygulamanızda da kod ile süre tutup bu bilgiyi elde edebilirsiniz.

Standart IBExpert kurulumu sorgu sonuçlarının tamamını getirmez. Ekrana sığacak kayıt sayısı kadarını getirir (yaklaşık 25-30 adet) siz ekranada gözükmeyen kayıtları görmek için aşağı inmek istediğinizde kalan kayıtları almak için sunucu ile iletişim kurar. Bu noktada sizin sorgunuz yavaş çalıştığı için "donma" etkisi gösteriyor olabilir. Bunu önlemek için Options -> Environment Options içinde SQL Editor kısmında Fetch All isimli kutuyu işaretlemeniz gerekir.

FirebirdSQL tarafında yapabileceklerinizden bazıları;
(1) "musteriler.Id not In (SELECT satislar.musteri_Id FROM satislar WHERE (satislar.tur_id=2))" ifadesini aşağıdaki örneğe benzer LEFT JOIN kullanacak şekilde değiştirmeyi deneyin. Bu tür kullanım şekli FirebirdSQL optimize edicisinin kafasını karıştırabilir. Kullanabileceği indeks olsa bile kullanmıyor olabilir. Mesela bu ifadeyi hiç kullanmadığınızda performans artıyor ise sorununuzun kaynağı bu ifadede yatıyordur.
(2) WHERE kelimesinde kullanılan kolonlar için indeks oluşturun (MUSTERILER tablosu: "Gizle" kolonu, "Id" kolonu, SATISLAR tablosu: "tur_id" kolonu). Ancak genel anlamda çok fazla indeks oluşturmaktan da kaçının.
(3) Kullandığınız FirebirdSQL sunucu sürümüne uygun optimize edilmiş ayar dosyalarını kullanın: https://ib-aid.com/en/optimized-firebird-configuration/

Yukarıdaki (1) numaralı öneri ile ilgili olarak:
Yaşadığınız yavaşlık sorununun bu madde olma ihtimali yüksek olduğuna inanıyorum. Bu anlamda LEFT JOIN kullanan örnek SQL:

Kod: Tümünü seç

SELECT 
  satislar.musteri_Id,
  musteriler.adi_soyadi,
  musteriler.adres,
  musteriler.telefon,
  musteriler.telefon1,
  SUM(((musteriler.satis+musteriler.taksitlisatis) - (musteriler.odemeler - musteriler.odemeler_taksit))) AS BAKIYE
FROM
  satislar
  LEFT JOIN MUSTERILER on musteriler.id = satislar.musteri_Id
WHERE 
 musteriler.Gizle=0 AND satislar.tur_id=2
GROUP BY 1,2,3,4,5
Örnek SQL için çalıştırma planı:

Kod: Tümünü seç

Plan
PLAN SORT (JOIN (SATISLAR NATURAL, MUSTERILER NATURAL))
Sizin kullandığınız sorgu için çalıştırma planı:

Kod: Tümünü seç

Plan
PLAN (SATISLAR NATURAL)
PLAN (SATISLAR NATURAL)
PLAN (MUSTERILER NATURAL)
Göreceğiniz üzere sizin kullandığınız sorgu satışlar tablosunu iki kere baştan sona okuyor. Tavsiye olarak verilen SQL ise her iki tabloyu tek seferde okuyor. Dolu veritabanı olmadığı için her iki sorgu arasındaki performans farkını ve her iki sorgu aynı sonucu veriyor mu kontrol etmek mümkün olmadı.

NOT: Örnek planlar FirebirdSQL 3.0.4 ile hazırlanmıştır. Farklı sürümlerde farklı sonuçlar alınabilir.
NOT2: Plan için kullanılan tablo yapıları aşağıda verilmiştir.

Kod: Tümünü seç

/* HIC INDEKS OLUSTURULMADI */
CREATE TABLE MUSTERILER (
    ID               INTEGER,
    ADI_SOYADI       VARCHAR(80),
    ADRES            VARCHAR(200),
    TELEFON          VARCHAR(20),
    TELEFON1         VARCHAR(20),
    SATIS            DECIMAL(18,4),
    TAKSITLISATIS    DECIMAL(18,4),
    ODEMELER         DECIMAL(18,4),
    ODEMELER_TAKSIT  DECIMAL(18,4),
    GIZLE            INTEGER
);


CREATE GENERATOR GEN_SATISLAR_ID;

/* SADECE AUTO INCREMENT KOLON ICIN INDEKS KULLANILDI */
CREATE TABLE SATISLAR (
    AUTOINC     INTEGER NOT NULL PRIMARY KEY,
    MUSTERI_ID  INTEGER,
    TUR_ID      INTEGER
);

SET TERM ^ ;

/* Trigger: SATISLAR_BI */
CREATE OR ALTER TRIGGER SATISLAR_BI FOR SATISLAR
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
  IF (NEW.AUTOINC IS NULL) THEN
    NEW.AUTOINC = GEN_ID(GEN_SATISLAR_ID,1);
END
^

SET TERM ; ^
Ertank hocam yanıtın için çok teşekkür ederim. Detaylı bir anlatım olmuş. Left join ve indexleme yaparak sorun çözüldü. Teşekkürler
baloglurecep
Üye
Mesajlar: 261
Kayıt: 21 Tem 2006 04:59
İletişim:

Re: İç içe sorgu yavaşlığı (ÇÖZÜLDÜ)

Mesaj gönderen baloglurecep »

Değerli arkadaşlar... destek olan,fikir veren tüm arkadaşlara teşekkürler ediyorum. Nihayetinde sorunu çözüme kavuşturduk. Bazen farklı bakış açısı ve yorumlamalar gerekebiliyor. Yazılımı bu yüzden ekip ekip işi olarak görüyorum her daim.
Şöyle bir kod yazarak işlemi tamamladım. tekrar tüm dostlara sağlıklı günler dilerim.

Kod: Tümünü seç

DateSeparator:='.';
 With  pFIBDataSet1 do
 begin
 Close;
 SelectSQL.Clear;
 SelectSQL.Add('select  musteriler.id,musteriler.adi_soyadi,musteriler.telefon1,(musteriler.satis+musteriler.taksitlisatis)-(musteriler.odemeler+musteriler.odemeler_taksit) AS BAKIYE');
 SelectSQL.Add('FROM musteriler musteriler left JOIN satislar satislar ON musteriler.id = satislar.musteri_id and satislar.tur_id = 2');
 SelectSQL.Add('and (satislar.tarih between :TARIH1 AND :TARIH2)');
 parambyname('TARIH1').ASDATETIME := bsSkinDateEdit1.DATE;
 parambyname('TARIH2').ASDATETIME := bsSkinDateEdit2.DATE;
 SelectSQL.Add('where satislar.id is null and musteriler.gizle = 0 ');
  if CheckBox1.Checked=true then
 SelectSQL.Add('and (musteriler.satis+musteriler.taksitlisatis)-(' +
   'musteriler.odemeler+musteriler.odemeler_taksit)>0 ');
 SelectSQL.Add('ORDER BY satislar.tarih');
 Open;
 end;

Bu kısımlar da eski iç içe select komutu ve yeni left inner joinli kullanım sonuçları ....

1- Bu iç içe select komutu ile yapılan sorgunun ibexpert ile çalışma süresi gösterimi :
Plan

PLAN (SATISLAR INDEX (SATISLAR_IDXTUR_ID))
PLAN (MUSTERILER INDEX (MUSTERILER_IDXGIZLE))

------ Performance info ------
Prepare time = 0ms
Execute time = 14s 688ms
Avg fetch time = 4,42 ms
Current memory = 807.960
Max memory = 937.216
Memory buffers = 2.048
Reads from disk to cache = 0
Writes from cache to disk = 0
Fetches from cache = 40.277.159


2- Bu da left inner join kullanırak yapılan sorgunun dönüt bilgileri (sorunumuzun çözülmüş halinin sonucu)

Plan
PLAN SORT (JOIN (MUSTERILER INDEX (MUSTERILER_IDXGIZLE), SATISLAR INDEX (SATISLAR_IDXMUSTERI_ID, SATISLAR_IDXTUR_ID)))

------ Performance info ------
Prepare time = 15ms
Execute time = 250ms
Avg fetch time = 1,09 ms
Current memory = 797.300
Max memory = 937.216
Memory buffers = 2.048
Reads from disk to cache = 0
Writes from cache to disk = 0
Fetches from cache = 22.141

Arada muazzam fark olup ayrıca donma, kasma problemi yok.
Cevapla