Bu SQL sorgu optimize

4 Cevap php

Bu SQL sorgusu midemi bulandırıyor. Ben yazmadım, ama bizim sunucularda sorunların büyük bir nedeni var. Ben birden fazla sorgu içine kadar bölünmüş ve PHP (gibi, RAND ()) üzerinden işleme bazı yapmaya hazırım.

$sql = "SELECT a.code, a.ad_id, a.position, a.type, a.image, a.url, a.height, a.width
	FROM " . AD_TABLE ." a, " . USER_GROUP_TABLE . " g
	WHERE (a.max_views >= a.views OR a.max_views = '0')
	AND (FIND_IN_SET(" .$forum_id. ", a.show_forums) > 0 OR a.show_all_forums = '1')
	AND g.user_id = " . $user->data['user_id'] . "
	AND FIND_IN_SET(g.group_id, a.groups)
	AND FIND_IN_SET(" . $user->data['user_rank'] . ", a.ranks)
	AND a.start_time < " . time() . "
	AND a.end_time > " . time() . "
	AND (a.clicks <= a.max_clicks OR a.max_clicks = '0')
	ORDER BY rand()";

Yeesh, ben yapıştırdıktan sonra iğrenç hissediyorum ...

EDIT: Aşağıda virgülle ayrılmış yukarıdaki biçimde, örnek bir sorguda "İZAH" sonuçlarıdır:

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
1,"SIMPLE","g","ref","user_id","user_id","3","const",6,"Using temporary; Using filesort"
1,"SIMPLE","a","ALL","max_views","","","",10,"Using where"

Öyle

4 Cevap

Burada üç büyük sorunları var:

  1. FIND_IN_SET.

    Bu bir dizin daha hızlı yapamaz, sargable değil. Bir çok-çok ilişkisi tablo (veya tablolar) oluşturun.

  2. a.start_time < GETDATE() AND a.end_time > GETDATE()

    MySQL bu optimize iyi değil. Sen geometri kutuları gibi timespans tutmak ve bir SPATIAL INDEX onlar üzerinde, bu çok daha hızlı olacaktır (gerçi az okunabilir) oluşturabilirsiniz

  3. ORDER BY RAND()

    (Yani tüm satırları değil, küçük, rasgele bir alt kümesini gerekmez) verileri örneklemek için bu kullanıyorsanız, bloguma bu makalede açıklanan bunu yapmak için daha verimli bir yolu vardır:

  • Sen do not have an explicit join tablo a ve tablo g arasındaki bu sorguda: sadece find_in_set ile ilgili (g.group_id, a.groups)
  • "A.groups" daki set ne kadar büyük, yani csv dize bir grup-kimliği içermiyor çoğu kez?
  • If 99% of the cases contain only 1 group, then make a foreach loop over "a.groups" in php and execute a real join (or probably it might eliminate your user_group_table altogether) from the query.
    • Eğer 1'den fazla grup üyeliğini taşıyan vakaların azınlık için, sorgu hala katılmak açık bir ok ile yapacak.

Bu php sınıf / işlevi biraz daha kod katacak.

Senin sorunun tarihleri ​​ile yatıyor tahmin ediyorum.

AND a.start_time < " . time() . "
AND a.end_time > " . time() . "

Ben bu alanlarda indeksler koyarak denemek ve görmek eğer yardımcı olur olacaktır. Tarihleri ​​karşılaştırarak veritabanı tablosundaki her satır karşılaştırmak neden olur.

Bunu çok çalıştırırsak .. deneyin ve (yerine dize konsatenasyonundan) bind değişkenleri kullanabilirsiniz .. bu yüzden sorgu her zaman ayrıştırılamaz zorunda değildir ..

edit: Üzgünüm .. MYSQL etiketi görmedim. Son zamanlarda değişti sürece ben MySQL hazırlanmış deyimleri sevdiğini sanmıyorum. Öte yandan ORACLE sever.